-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tag docker target images with a unique name #31
Conversation
docker/docker.ts
Outdated
// this tag doesn't require actually pushing the image, nor does it require communicating with | ||
// some external system, making it suitable for unique identification, even during preview. | ||
// This also means that if docker produces a new imageId, we'll get a new name here, ensuring that | ||
// resources will be appropriately replaced. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What resources?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will document. but htings like cloud.Service.
}); | ||
|
||
this.id = imageData.apply(d => d.id); | ||
this.digest = imageData.apply(d => d.digest); | ||
this.imageName = imageData.apply(d => d.uniqueTargetName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so the idea here is that consumers can key off of imageName
and expect to get replaced or updated if the image changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup!
docker/docker.ts
Outdated
cacheFrom = pullCacheAsync(imageName, cacheFromParam, login, repositoryUrl, logResource); | ||
} else { | ||
cacheFrom = Promise.resolve(undefined); | ||
cacheFrom = await pullCacheAsync(imageName, cacheFromParam, login, repositoryUrl, logResource); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did we have to change this? I'm a little nervous that this subtly changes some execution behaviour that we depended on. (We now block on the pullCacheAsync
instead of running it in parallel with other work below?). Given that we don't have much test coverage around the cacheFrom
support, I'm inclined to not change it unless we have to - and if we do, to ideally add some additional test coverage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not. I'll revert this. It seemed like this existed at a time when we didn't have async/await here and were instead using Outputs. We cleaned that up at one point, but this stuff still remained. changing this made things clearer for me. However, you're correct this isn't necessary, so i'll revert :)
docker/docker.ts
Outdated
|
||
if (!repositoryUrl) { | ||
throw new ResourceError("Expected repository URL to be defined during push", logResource); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this? The parameter is marked non-optional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was preserving/moving this from existing code. But you're right that it shouldn't be necessary in either case. will remove.
docker/docker.ts
Outdated
// some external system, making it suitable for unique identification, even during preview. | ||
// This also means that if docker produces a new imageId, we'll get a new name here, ensuring that | ||
// resources (like docker.Image and cloud.Service) will be appropriately replaced. | ||
const uniqueTargetName = createTargetName(repositoryUrl, tag, imageId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps uniqueImageName
or uniqueTaggedImageName
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure.
docker/docker.ts
Outdated
} | ||
} | ||
|
||
return buildResult.id; | ||
return uniqueTargetName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is returning this instead of id
a breaking change? I think not - that it is still a unique identifier that is sufficient for any use case of the previous return value - but just want to make sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right. it's only 'breaking' in the sense if somoene took a hard dependency on this exactly being docker's image ID. but we never doc'ed that, so it would be their issue for doing this. It's not an issue for our own docker.Image or cloud.Service consumers.
docker/docker.ts
Outdated
// A tag name must be valid ASCII and may contain lowercase and uppercase letters, digits, | ||
// underscores, periods and dashes. A tag name may not start with a period or a dash and may | ||
// contain a maximum of 128 characters. | ||
const fullTag = pieces.join("-").replace(/[^-_.a-zA-Z0-9]/, "").substr(0, 128); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have to do this? If the user provided a tag - it's their fault if it isn't legal and I don't think we should try to "fix" it. If the imageID from Docker is not legal to use here - we should probably do a more intentional transformation on it. Or if we are paranoid that it's format may change arbitrarily - at least only do this on the imageId component?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's a fair point. The major concern is simply the length. If the caller provides a long tag length, and we jam on the hash, and we go past 128, then that's going to blowup. But perhaps we just let that blowup in that case....
// That's fine, just move onto the next stage. | ||
const { code } = await runCommandThatCanFail( | ||
"docker", ["pull", image], logResource, | ||
/*reportFullCommand:*/ true, /*reportErrorAsWarning:*/ true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this fixing some latent bug in the existing code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will doc. The idea here is that 'pulling' during the cacheefrom code will fail the command the first time (when things aren't cached locally). This ends up with a pulumi update that succeeds, but which prints an error at the end. This simply changes things to not print out that command failure as an 'error' but instead as a 'warning'.
// Trim off the hash kind if there is one. | ||
let imageId = inspectResult.trim(); | ||
const colonIndex = imageId.lastIndexOf(":"); | ||
imageId = colonIndex < 0 ? imageId : imageId.substr(colonIndex + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we trim this off? Shouldn't we return the full imageID
just so that we expose exactly what Docker uses as the image ID?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ImageID is "algorithm:hex". I trim off the algorithm:
part since we can't have colons in the tag portion. As we want just a unique tag, the hex portion is sufficient. I could include the algorithm. but it seems really superfluous...
Thoughts?
p.stdin.end(stdin); | ||
} | ||
|
||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes me nervous that we are touching this code - it has historically had very subtle issues. Did we have to for this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lukehoban ptal. Note: i recommend just looking at the after form of this function, not necessarily the diff. It's actually become quite simple and i think has a lot of behaviors that will be more desirable than what we had before.
docker/image.ts
Outdated
/** | ||
* The pushed image digest. | ||
*/ | ||
public digest: pulumi.Output<string | undefined>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are breaking changes. Can we mitigate these by deprecating these properties and publishing something sufficiently correct for these two values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup. i'll do that.
@lukehoban Ready for anothe rpass. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - just one remaining concern on the exec
changes.
}); | ||
|
||
p.on("close", code => { | ||
logStdErrMessages(code); | ||
finish(code); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's safe to call finish in both error
and close
. I believe both will frequently get called?
And it looks like we now never reject
- is that really intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. have put a lot of effort in doc'ing this. ptal @lukehoban i think the code is where we want it, and actually will give ideal results here.
docker/image.ts
Outdated
*/ | ||
public registryServer: pulumi.Output<string | undefined>; | ||
|
||
/**@deprecated This will have the same value as [imageName], but will be removed in the future. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting looks a little funny here - but maybe that's okay.
We now tag all our docker pushed builds with a tag equal to their image-id. i.e. we'll end up tagging a build like so:
<awsid>.dkr.ecr.<region>.amazonaws.com/image-name:<image-id>
or<awsid>.dkr.ecr.<region>.amazonaws.com/image-name:<user-tag>-<image-id>
By adding teh image-id into the tag name, we then have a unique and stable name for the target build that can be known at preview time. It also means that any time docker produces a new image id, we end up replacing the image. This helps us avoid needing to hack things in at the cloud layer where we place hte image-id in the environment variable map in order to get pulumi to want to replace the cloud.Service resource.
As part of this change docker.buildAndPushImage now retursn that entire target name, instead of just returning the image-id like it used to before.