Skip to content
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

feat(manifest): allow additional docker build overrides in manifest #1059

Merged
merged 54 commits into from
Aug 5, 2020

Conversation

bvtujo
Copy link
Contributor

@bvtujo bvtujo commented Jun 26, 2020

This PR adds the ability to specify docker compose style build directives in the manifest.

For example, customers can now specify dockerfile, context, and args underneath the build key in the manifest, or simply specify a dockerfile path at build.

Example 1

image:
  build: path/to/MyDockerfile

This manifest will build the dockerfile specified at build, using the directory path/to as the build context.

Example 2

image:
  build:
    dockerfile: path/to/MyDockerfile
    context: path/to/service/code
    args:
      ABC: 123
      arg2: value2

This example will build the dockerfile at path/to/MyDockerfile using the directory path/to/service/code as the build context and passing the args as --build-arg flags.

Fixes #822 and #1030

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@bvtujo bvtujo added the do-not-merge Pull requests that mergify shouldn't merge until the requester allows it. label Jun 26, 2020
@bvtujo bvtujo requested a review from a team as a code owner June 26, 2020 19:07
@bvtujo bvtujo self-assigned this Jun 26, 2020
@bvtujo bvtujo requested a review from kohidave June 26, 2020 19:07
Copy link
Contributor

@iamhopaul123 iamhopaul123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if adding context field will help completely fix our problematic docker build. Currently if your pwd is not the root workspace then it is very likely svc deploy will fail because our dockerfile path is a relative one and ./subscribers/Dockerfile doesn't exist since . denotes different path. #1030

Have a use case for that. If the user has two services "frontend" and "backend". After they deploy those services, the frontend service has bug and they'd like to cd into the folder to run svc show/logs/status easier. However, they have to cd back to the workspace root in order to run svc deploy again, which might not make sense.

I think maybe we could probably completely get rid of the . and require users to put their dockerfile in the workspace. The build field will be like build: subscribers/myDockerfile. What do you think?

@bvtujo
Copy link
Contributor Author

bvtujo commented Jun 26, 2020

@iamhopaul123 My thought was that our current behavior is pretty bad especially for go. Consider the following use cases:

  1. The golang project layout standard suggests that you should keep your dockerfiles in a separate build/package directory (build/package/Dockerfile_fe e.g.), not the root of the service code. Without a context, these builds will assume that the docker context directory is build/package. This contains no code files, though, and will fail.
  2. Also using go best practices, any service whose main function lives at cmd/svcName/main.go will also fail to build under any cirumstances unless the dockerfile and context directory are at the workspace root. If the dockerfile is located at cmd/svcName/Dockerfile, the build context will be inferred to be cmd/svcName and the build will fail if the main() function imports any packages at pkg/* or internal/pkg/*.

This is a separate problem from svc deploy failing if it is not run from the workspace root.

@iamhopaul123
Copy link
Contributor

@iamhopaul123 My thought was that our current behavior is pretty bad especially for go. Consider the following use cases:

  1. The golang project layout standard suggests that you should keep your dockerfiles in a separate build/package directory (build/package/Dockerfile_fe e.g.), not the root of the service code. Without a context, these builds will assume that the docker context directory is build/package. This contains no code files, though, and will fail.
  2. Also using go best practices, any service whose main function lives at cmd/svcName/main.go will also fail to build under any cirumstances unless the dockerfile and context directory are at the workspace root. If the dockerfile is located at cmd/svcName/Dockerfile, the build context will be inferred to be cmd/svcName and the build will fail if the main() function imports any packages at pkg/* or internal/pkg/*`.

This is a separate problem from svc deploy failing if it is not run from the workspace root.

Oh then that makes sense to me. Thanks for explaining!

@efekarakus efekarakus changed the title feat(manifest): Add context field for docker build configurability feat(manifest): add context field for docker build configurability Jun 29, 2020
@kohidave kohidave requested a review from a team July 7, 2020 22:27
@bvtujo
Copy link
Contributor Author

bvtujo commented Jul 13, 2020

This PR now includes a fix for #1030. Dockerfile paths and contexts are interpreted from the workspace root (one level up from the copilot dir) instead of pwd. Contexts are now backwards compatible--if a manifest does not contain a context field, we call filepath.Dir() on the dockerfile path, and pass that value to docker build.

Copy link
Contributor

@iamhopaul123 iamhopaul123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks awesome! Just two suggestions

internal/pkg/cli/svc_deploy.go Outdated Show resolved Hide resolved
internal/pkg/docker/docker.go Outdated Show resolved Hide resolved
internal/pkg/manifest/backend_svc.go Show resolved Hide resolved
@bvtujo bvtujo removed the do-not-merge Pull requests that mergify shouldn't merge until the requester allows it. label Jul 13, 2020
Copy link
Contributor

@iamhopaul123 iamhopaul123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚢

@bvtujo bvtujo added the do-not-merge Pull requests that mergify shouldn't merge until the requester allows it. label Jul 14, 2020
@bvtujo
Copy link
Contributor Author

bvtujo commented Jul 14, 2020

This PR should be updated to ensure
a) the pipeline buildspec takes advantage of new fields
b) that we use Docker Compose syntax for additional configuration in our "build" field. For example:

image:
  build:
    context: context/dir
    dockerfile: path/to/dockerfile

As described in https://docs.docker.com/compose/compose-file/#build

I've added the do-not-merge label until we can work through our next decision on this matter

Copy link
Contributor

@efekarakus efekarakus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only minor comments, also great tests!

Comment on lines 299 to 301
// Assemble other parameters for build input.
dockerBuildInput.URI = uri
dockerBuildInput.ImageTag = o.ImageTag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: what do you think of moving this to within getBuildArgs()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Respository now handles making the URI, and getBuildArgs adds the image tag.

m.EXPECT().ReadServiceManifest("serviceA").Times(1).Return(mockManifest, nil)
},
},
"using simple buildstring (backwards compatible)": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

func (r Runner) Build(uri, path, imageTag string, additionalTags ...string) error {
dfDir := filepath.Dir(path)
// BuildArguments holds the arguments we can pass in as flags from the manifest.
type BuildArguments struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v nice comments!

internal/pkg/docker/docker_test.go Outdated Show resolved Hide resolved
internal/pkg/manifest/backend_svc.go Outdated Show resolved Hide resolved
internal/pkg/manifest/svc.go Outdated Show resolved Hide resolved
internal/pkg/manifest/svc.go Outdated Show resolved Hide resolved
Build *string `yaml:"build"` // Path to the Dockerfile.
Build BuildArgsOrString `yaml:"build"` // Path to the Dockerfile.
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I am not sure about returning a docker.BuildArguments instead of a manifest.DockerBuildArgs from the manifest pkg, but I'm okay with keeping it as is.

internal/pkg/manifest/svc.go Outdated Show resolved Hide resolved
Copy link
Contributor

@kohidave kohidave left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THANK YOU.

if !ok {
return "", fmt.Errorf("service %s does not have a dockerfile path", o.Name)
return nil, fmt.Errorf("service %s does not have required method Build()", o.Name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe something our users could grok easier:

manifest for service %s does not have a `build` section

Or something like that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a weird error because it specifically has to do with the interface conversion to the dfArgs interface and making sure that the unmarshaled yaml fits into a BackendService or LoadBalancedWebService struct with the right methods attached.

}
return mf.DockerfilePath(), nil
wsRoot := filepath.Dir(copilotDir)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? I believe CopilotDirPath returns a directory.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because we want the workspace root, not the Copilot dir. filepath.Dir is equivalent to the shell dirname command.

"success with build args": {
path: mockPath,
args: map[string]string{
"GOPROXY": "direct",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love it

}

//BuildArgs returns a docker.BuildArguments object given a ws root directory.
func (s *LoadBalancedWebService) BuildArgs(wsRoot string) *DockerBuildArgs {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add this method to Service struct, so we don't always have to redefine it for every pattern? Or is it not possible (no prob if it's not, just curioust)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried but it's weird, because the method needs access to the Image member of the LoadBalancedWebService or BackendService struct, but I don't know how to cast a particular member of an unmarshaled struct to an interface instead of just the whole struct

@bvtujo bvtujo removed the do-not-merge Pull requests that mergify shouldn't merge until the requester allows it. label Aug 4, 2020
@mergify mergify bot merged commit 5584131 into aws:master Aug 5, 2020
@bvtujo bvtujo changed the title feat(manifest): allow docker-compose style docker build config in manifest feat(manifest): allow additional docker build overrides in manifest Aug 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

internal/pkg/docker should be more flexible with context and dockerfile paths
4 participants