-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Initialize workspaces with additional file content #4428
Conversation
d10df5e
to
1cb3a6f
Compare
f3076b8
to
490fd65
Compare
/werft run 👍 started the job as gitpod-build-se-external-config.9 |
97ea581
to
221da19
Compare
35886e4
to
1959053
Compare
/werft run 👍 started the job as gitpod-build-se-external-config.17 |
0234ebf
to
9bdd722
Compare
9bdd722
to
2a14026
Compare
} | ||
if (!customConfig) { | ||
|
||
// try and find config file in the context repo or remote in |
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.
for the reviewers: section below hasn't changed besides the indent because of the if.
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.
Tip: Adding ?w=1
at the end of the review URL ignores all whitespace changes (i.e. re-indented blocks will appear unchanged).
const hostContext = this.hostContextProvider.get(context.repository.host); | ||
if (!hostContext || !hostContext.services) { | ||
throw new Error(`Cannot fetch workspace image source for host: ${context.repository.host}`); | ||
} | ||
const lastDockerFileSha = await hostContext.services.fileProvider.getLastChangeRevision(context.repository, context.revision, user, imgcfg.file); | ||
const lastDockerFileShaPromise = hostContext.services.fileProvider.getLastChangeRevision(context.repository, context.revision, user, imgcfg.file); |
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 need to make two requests for the time being, to support the current resolution of image refs. Once we have rebuilt most images, we can remove the lastChangeRevision
part.
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.
If we resolved the base image here already we could check which case (lastDockerFileSha
or this.getContentSHA(dockerFileContent)
) resolves to a built image. This behaviour is what I referred to as "move the legacy behaviour to server" above.
2a14026
to
b97272b
Compare
How are we dealing with environment variables and tasks? Right now the env vars assume that the workspace their pattern matches for can be trusted. With this change if I can make someone click on a link I can quite easily get their secrets because I can bring my own tasks. |
I think this is a general problem that exists with all our workspace start URLs. The underlying issue is that supervisor runs processes and it is not obvious what those processes will be from looking at a link. I suggest we introduce something where we ask users if they trust certain sources to allow task execution. |
Indeed it does. What the Asking users if they want to execute the task might be one way - and is indeed a trade-off between friction and security. |
message BuildSourceDockerfile { | ||
contentservice.WorkspaceInitializer source = 1; | ||
string dockerfile_version = 2; | ||
string dockerfile_path = 3; | ||
string context_path = 4; | ||
string dockerfile_content_sha = 5; |
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 could re-use the dockerfile_version
- I don't think we need to change the protocol here.
The only difference is that before the version was the commit hash, now it's the SHA hash.
If we moved the "legacy" behaviour to server, that is.
@@ -224,6 +224,10 @@ func (b *DockerBuilder) getBaseImageRef(ctx context.Context, bs *api.BuildSource | |||
return b.getAbsoluteImageRef(ctx, src.Ref.Ref, allowedAuth) | |||
|
|||
case *api.BuildSource_File: | |||
if src.File.DockerfileContentSha != "" { | |||
return fmt.Sprintf("%s:%x", b.Config.BaseImageRepository, src.File.DockerfileContentSha), nil |
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.
The manifest approach below takes more into account than just the Dockerfile version. Using just the content hash ignores the context entirely - the same Dockerfile from two separate repositories with different context would get the same "build".
This could even leak sensitive details because one user might get the build of another (same Dockerfile content, but different context including repo).
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.
Note: because GitHub doesn't let me put a comment in line 238 ... you'd have to expand the if
to include the CompositeInitializer
etc.
const hostContext = this.hostContextProvider.get(context.repository.host); | ||
if (!hostContext || !hostContext.services) { | ||
throw new Error(`Cannot fetch workspace image source for host: ${context.repository.host}`); | ||
} | ||
const lastDockerFileSha = await hostContext.services.fileProvider.getLastChangeRevision(context.repository, context.revision, user, imgcfg.file); | ||
const lastDockerFileShaPromise = hostContext.services.fileProvider.getLastChangeRevision(context.repository, context.revision, user, imgcfg.file); |
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.
If we resolved the base image here already we could check which case (lastDockerFileSha
or this.getContentSHA(dockerFileContent)
) resolves to a built image. This behaviour is what I referred to as "move the legacy behaviour to server" above.
source = initializer; | ||
disp.push(disposable); | ||
} else { | ||
//TODO we cannot change this initializer structure now because it is part of how baserefs are computed in image-builder. |
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.
nit:
//TODO we cannot change this initializer structure now because it is part of how baserefs are computed in image-builder. | |
// TODO(se): we cannot change this initializer structure now because it is part of how baserefs are computed in image-builder. |
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'd propose we update the behaviour in image builder to accompany the new initializer. Also, the current change on how the baseRef is computed ignores the initializer entirely.
let contextPath = checkoutLocation; | ||
let dockerFilePath = checkoutLocation + '/' + imgsrc.dockerFilePath; |
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.
This seems to be at odds with the comment above. Either the comment or the code is wrong :)
/werft run 👍 started the job as gitpod-build-se-external-config.26 |
b97272b
to
09a343c
Compare
/werft run 👍 started the job as gitpod-build-se-external-config.28 |
d2ec3fa
to
3b8cf5a
Compare
Introduces an AdditionalContentContext and the corresponding Initializer.
3b8cf5a
to
c10fb46
Compare
Thanks, @csweichel! I have updated the PR as discussed, could you have a look again? |
/werft run 👍 started the job as gitpod-build-se-external-config.36 |
/werft run 👍 started the job as gitpod-build-se-external-config.37 |
a6cdde6
to
3fa9438
Compare
} | ||
manifest[prefix+"Files"] = fmt.Sprintf("%x", hash.Sum(nil)) |
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.
Wouldn't this trigger docker builds on every change in .gitpod.yml
(or other files)? I believe this would be too aggressive.
3fa9438
to
a7c2638
Compare
to the new FileDownloadInitializer
a7c2638
to
5436968
Compare
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
string digest = 3; | ||
} | ||
repeated FileInfo files = 1; | ||
string target_location = 2; |
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.
beware that all initializer are executed relative to a target location themselves, i.e. all paths in an initializer are always relative to that location.
The comment in line 30 implies that target_location
can be absolute, when in reality it cannot.
for _, info := range ws.FilesInfos { | ||
contents, err := downloadFile(ctx, info.url) | ||
if err != nil { | ||
tracing.LogError(span, xerrors.Errorf("cannot download file '%s' from '%s': %w", info.filePath, info.url, err)) |
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 it intentional that when downloading a file fails the content init does not fail?
return src, nil | ||
} | ||
|
||
func downloadFile(ctx context.Context, url string) (content []byte, err error) { |
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.
downloadFile
should write the file to disk immediately and not keep it in RAM first.
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.
also, we need to respect the digest
func (e *CompositeInitializer) Run(ctx context.Context, mappings []archive.IDMapping) (csapi.WorkspaceInitSource, error) { | ||
_, ctx = opentracing.StartSpanFromContext(ctx, "CompositeInitializer.Run") | ||
for _, init := range e.Initializer { | ||
init.Run(ctx, mappings) |
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.
if one initializer fails the others should fail, too
To allow starting workspaces based on a config that doesn't sit in Git, this PR introduces the possibility to pass file contents when creating a workspace, which overlays/overwrites the file system.
This is going to be used for passing configuration stored in projects. A prefix context
additionalcontent
(used below) is introduced for testing purposes, only.Testing
A link that puts a
.gitpod.yml
and.gitpod.Dockerfile
on top: https://se-external-config.staging.gitpod-dev.com/#/additionalcontent/eyIuZ2l0cG9kLnltbCI6ImltYWdlOlxuICBmaWxlOiAuZ2l0cG9kLkRvY2tlcmZpbGVcblxudGFza3M6XG4gIC0gY29tbWFuZDogZWNobyBcInRlc3RcIj5teWZpbGUudHh0IiwiLmdpdHBvZC5Eb2NrZXJmaWxlIjoiRlJPTSBnaXRwb2Qvd29ya3NwYWNlLWZ1bGxcblxuUlVOIC9ob21lL2dpdHBvZC8uY2FyZ28vYmluL2NhcmdvIGluc3RhbGwgbWRib29rIC0tdmVycyAwLjEuN1xuIn0=/https://github.com/gitpod-io/template-sveltejsSame additional content but different repo, which triggers an image build on its own because the repo is different: https://se-external-config.staging.gitpod-dev.com/#/additionalcontent/eyIuZ2l0cG9kLnltbCI6ImltYWdlOlxuICBmaWxlOiAuZ2l0cG9kLkRvY2tlcmZpbGVcblxudGFza3M6XG4gIC0gY29tbWFuZDogZWNobyBcInRlc3RcIj5teWZpbGUudHh0IiwiLmdpdHBvZC5Eb2NrZXJmaWxlIjoiRlJPTSBnaXRwb2Qvd29ya3NwYWNlLWZ1bGxcblxuUlVOIC9ob21lL2dpdHBvZC8uY2FyZ28vYmluL2NhcmdvIGluc3RhbGwgbWRib29rIC0tdmVycyAwLjEuN1xuIn0=/https://github.com/gitpod-io/spring-petclinic