diff --git a/pkg/build/buildkit/build.go b/pkg/build/buildkit/build.go index 485311f..ece8e68 100644 --- a/pkg/build/buildkit/build.go +++ b/pkg/build/buildkit/build.go @@ -111,12 +111,12 @@ func (b *BuildKit) buildFromAppSourceFiles(ctx context.Context, r *pb.BuildReque return nil, err } - // NOTE(nettoclaudio): Some platforms doesn't require an user-defined Procfile (e.g. go, java, static, etc). + // NOTE(nettoclaudio): Some platforms don't require an user-defined Procfile (e.g. go, java, static, etc). // So we need to retrieve the default Procfile from the platform image. if appFiles.Procfile == "" { fmt.Fprintln(w, "User-defined Procfile not found, trying to extract it from platform's container image") - tc, err := b.extractTsuruConfigsFromContainerImage(ctx, r.DestinationImages[0]) + tc, err := b.extractTsuruConfigsFromContainerImage(ctx, r.DestinationImages[0], build.DefaultTsuruPlatformWorkingDir) if err != nil { return nil, err } @@ -177,7 +177,7 @@ func (b *BuildKit) buildFromContainerImage(ctx context.Context, r *pb.BuildReque return nil, err } - appFiles, err := b.callBuildKitToExtractTsuruConfigs(ctx, tmpDir) + appFiles, err := b.callBuildKitToExtractTsuruConfigs(ctx, tmpDir, imageConfig.WorkingDir) if err != nil { return nil, err } @@ -186,17 +186,17 @@ func (b *BuildKit) buildFromContainerImage(ctx context.Context, r *pb.BuildReque return appFiles, nil } -func (b *BuildKit) extractTsuruConfigsFromContainerImage(ctx context.Context, image string) (*pb.TsuruConfig, error) { +func (b *BuildKit) extractTsuruConfigsFromContainerImage(ctx context.Context, image, workingDir string) (*pb.TsuruConfig, error) { tmpDir, cleanFunc, err := generateBuildLocalDir(ctx, b.opts.TempDir, fmt.Sprintf("FROM %s", image), nil, nil, nil) if err != nil { return nil, err } defer cleanFunc() - return b.callBuildKitToExtractTsuruConfigs(ctx, tmpDir) + return b.callBuildKitToExtractTsuruConfigs(ctx, tmpDir, workingDir) } -func (b *BuildKit) callBuildKitToExtractTsuruConfigs(ctx context.Context, localContextDir string) (*pb.TsuruConfig, error) { +func (b *BuildKit) callBuildKitToExtractTsuruConfigs(ctx context.Context, localContextDir, workingDir string) (*pb.TsuruConfig, error) { eg, ctx := errgroup.WithContext(ctx) pr, pw := io.Pipe() // reader/writer for tar output @@ -231,7 +231,7 @@ func (b *BuildKit) callBuildKitToExtractTsuruConfigs(ctx context.Context, localC var tc *pb.TsuruConfig eg.Go(func() error { var err error - tc, err = build.ExtractTsuruAppFilesFromContainerImageTarball(ctx, pr) + tc, err = build.ExtractTsuruAppFilesFromContainerImageTarball(ctx, pr, workingDir) return err }) @@ -387,21 +387,23 @@ func (b *BuildKit) buildFromContainerFile(ctx context.Context, r *pb.BuildReques return nil, err } - tc, err := b.extractTsuruConfigsFromContainerImage(ctx, r.DestinationImages[0]) - if err != nil { - return nil, err - } - var insecureRegistry bool if r.PushOptions != nil { insecureRegistry = r.PushOptions.InsecureRegistry } - tc.ImageConfig, err = extractContainerImageConfigFromImageManifest(ctx, r.DestinationImages[0], insecureRegistry) + ic, err := extractContainerImageConfigFromImageManifest(ctx, r.DestinationImages[0], insecureRegistry) if err != nil { return nil, err } + tc, err := b.extractTsuruConfigsFromContainerImage(ctx, r.DestinationImages[0], ic.WorkingDir) + if err != nil { + return nil, err + } + + tc.ImageConfig = ic + return tc, nil } diff --git a/pkg/build/buildkit/build_test.go b/pkg/build/buildkit/build_test.go index fbd0bbc..c8ae055 100644 --- a/pkg/build/buildkit/build_test.go +++ b/pkg/build/buildkit/build_test.go @@ -579,6 +579,46 @@ CMD ["--port", "8080"] }, }, appFiles) }) + + t.Run("using a different working directory, should get Procfile and Tsuru YAML from that", func(t *testing.T) { + destImage := baseRegistry(t, "my-app", "") + + dockerfile := `FROM busybox + +RUN set -xef \ + && mkdir -p /var/my-app \ + && echo "web: /path/to/server.sh --port 8888" > /var/my-app/Procfile \ + && echo -e "healthcheck:\n path: /healthz\n" > /var/my-app/tsuru.yaml + +WORKDIR /var/my-app + +EXPOSE 8888/tcp +` + + req := &pb.BuildRequest{ + Kind: pb.BuildKind_BUILD_KIND_APP_BUILD_WITH_CONTAINER_FILE, + App: &pb.TsuruApp{ + Name: "my-app", + }, + DestinationImages: []string{destImage}, + Containerfile: string(dockerfile), + PushOptions: &pb.PushOptions{ + InsecureRegistry: registryHTTP, + }, + } + + appFiles, err := NewBuildKit(bc, BuildKitOptions{TempDir: t.TempDir()}).Build(context.TODO(), req, os.Stdout) + require.NoError(t, err) + assert.Equal(t, &pb.TsuruConfig{ + Procfile: "web: /path/to/server.sh --port 8888\n", + TsuruYaml: "healthcheck:\n path: /healthz\n\n", + ImageConfig: &pb.ContainerImageConfig{ + Cmd: []string{"sh"}, + ExposedPorts: []string{"8888/tcp"}, + WorkingDir: "/var/my-app", + }, + }, appFiles) + }) } func compressGZIP(t *testing.T, path string) []byte { diff --git a/pkg/build/helpers.go b/pkg/build/helpers.go index 996d666..e7392fa 100644 --- a/pkg/build/helpers.go +++ b/pkg/build/helpers.go @@ -45,11 +45,18 @@ func IsTsuruYaml(filename string) bool { type TsuruYamlCandidates map[string]string -func (c TsuruYamlCandidates) String() string { - for _, dir := range TsuruConfigDirs { +func (c TsuruYamlCandidates) Pick(workingDir string) string { + dirs := make([]string, 0, (len(TsuruConfigDirs) + 1)) + + if workingDir != "" { + dirs = append(dirs, workingDir) // added first to get higher precedence + } + + dirs = append(dirs, TsuruConfigDirs...) + + for _, dir := range dirs { for _, baseName := range TsuruYamlNames { filename := filepath.Join(dir, baseName) - if s, found := c[filename]; found { return s } @@ -65,8 +72,16 @@ func IsProcfile(filename string) bool { type ProcfileCandidates map[string]string -func (c ProcfileCandidates) String() string { - for _, dir := range TsuruConfigDirs { +func (c ProcfileCandidates) Pick(workingDir string) string { + dirs := make([]string, 0, (len(TsuruConfigDirs) + 1)) + + if workingDir != "" { + dirs = append(dirs, workingDir) // added first to get higher precedence + } + + dirs = append(dirs, TsuruConfigDirs...) + + for _, dir := range dirs { filename := filepath.Join(dir, ProcfileName) if s, found := c[filename]; found { return s @@ -118,12 +133,12 @@ func ExtractTsuruAppFilesFromAppSourceContext(ctx context.Context, r io.Reader) } return &pb.TsuruConfig{ - Procfile: procfile.String(), - TsuruYaml: tsuruYaml.String(), + Procfile: procfile.Pick(DefaultTsuruPlatformWorkingDir), + TsuruYaml: tsuruYaml.Pick(DefaultTsuruPlatformWorkingDir), }, nil } -func ExtractTsuruAppFilesFromContainerImageTarball(ctx context.Context, r io.Reader) (*pb.TsuruConfig, error) { +func ExtractTsuruAppFilesFromContainerImageTarball(ctx context.Context, r io.Reader, workingDir string) (*pb.TsuruConfig, error) { if err := ctx.Err(); err != nil { return nil, err } @@ -158,8 +173,8 @@ func ExtractTsuruAppFilesFromContainerImageTarball(ctx context.Context, r io.Rea } return &pb.TsuruConfig{ - Procfile: procfile.String(), - TsuruYaml: tsuruYaml.String(), + Procfile: procfile.Pick(workingDir), + TsuruYaml: tsuruYaml.Pick(workingDir), }, nil } diff --git a/pkg/build/helpers_test.go b/pkg/build/helpers_test.go index 4eb94d2..3bd5d5b 100644 --- a/pkg/build/helpers_test.go +++ b/pkg/build/helpers_test.go @@ -47,11 +47,12 @@ func TestIsTsuruYaml(t *testing.T) { } } -func TestTsuruYamlCandidates_String(t *testing.T) { +func TestTsuruYamlCandidates_Pick(t *testing.T) { t.Parallel() cases := []struct { candidates TsuruYamlCandidates + workingDir string expected string }{ {}, @@ -112,20 +113,41 @@ func TestTsuruYamlCandidates_String(t *testing.T) { }, expected: "# Tsuru YAML from tsuru.yaml", }, + { + workingDir: "/var/www/html", + candidates: TsuruYamlCandidates{ + "/var/www/html/tsuru.yaml": "# Tsuru YAML from tsuru.yaml", + "/home/application/current/tsuru.yaml": "--------------------", + "/app/user/tsuru.yaml": "--------------------", + "/tsuru.yaml": "--------------------", + }, + expected: "# Tsuru YAML from tsuru.yaml", + }, + { + workingDir: "/not/found", + candidates: TsuruYamlCandidates{ + "/var/www/html/tsuru.yaml": "--------------------", + "/home/application/current/tsuru.yaml": "# Tsuru YAML from tsuru.yaml", + "/app/user/tsuru.yaml": "--------------------", + "/tsuru.yaml": "--------------------", + }, + expected: "# Tsuru YAML from tsuru.yaml", + }, } for _, tt := range cases { t.Run("", func(t *testing.T) { - assert.Equal(t, tt.expected, tt.candidates.String()) + assert.Equal(t, tt.expected, tt.candidates.Pick(tt.workingDir)) }) } } -func TestProcfileCandidates_String(t *testing.T) { +func TestProcfileCandidates_Pick(t *testing.T) { t.Parallel() cases := []struct { candidates ProcfileCandidates + workingDir string expected string }{ {}, @@ -158,10 +180,32 @@ func TestProcfileCandidates_String(t *testing.T) { "/home/application/current/demo/Procfile": "--------------------", }, }, + { + workingDir: "/var/www/html", + candidates: ProcfileCandidates{ + "/var/www/html/Procfile": "web: ./path/to/server.sh --port ${PORT}", + "/home/application/current/Procfile": "--------------------", + "/app/user/Procfile": "--------------------", + "/Procfile": "--------------------", + }, + expected: "web: ./path/to/server.sh --port ${PORT}", + }, + { + workingDir: "/not/found", + candidates: ProcfileCandidates{ + "/var/www/html/Procfile": "--------------------", + "/home/application/current/Procfile": "web: ./path/to/server.sh --port ${PORT}", + "/app/user/Procfile": "--------------------", + "/Procfile": "--------------------", + }, + expected: "web: ./path/to/server.sh --port ${PORT}", + }, } for _, tt := range cases { - assert.Equal(t, tt.expected, tt.candidates.String()) + t.Run("", func(t *testing.T) { + assert.Equal(t, tt.expected, tt.candidates.Pick(tt.workingDir)) + }) } } @@ -266,7 +310,7 @@ func TestExtractTsuruAppFilesFromContainerImageTarball(t *testing.T) { for _, tt := range cases { t.Run("", func(t *testing.T) { require.NotNil(t, tt.file) - tsuruFiles, err := ExtractTsuruAppFilesFromContainerImageTarball(context.TODO(), tt.file(t)) + tsuruFiles, err := ExtractTsuruAppFilesFromContainerImageTarball(context.TODO(), tt.file(t), "") if err != nil { require.EqualError(t, err, tt.expectedError) return