forked from sigstore/cosign
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(dockerfile): add resolve command
Fixes sigstore#648 Fixes sigstore#707 Signed-off-by: Furkan <furkan.turkal@trendyol.com> Co-authored-by: Batuhan <batuhan.apaydin@trendyol.com>
- Loading branch information
1 parent
5e261dc
commit 4e38dab
Showing
5 changed files
with
204 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package dockerfile | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"context" | ||
"flag" | ||
"fmt" | ||
"github.com/google/go-containerregistry/pkg/name" | ||
"github.com/sigstore/cosign/pkg/oci/remote" | ||
"io" | ||
"os" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
// ResolveDockerfileCommand rewrites the Dockerfile | ||
// base images to FROM <digest>. | ||
type ResolveDockerfileCommand struct { | ||
Output string | ||
} | ||
|
||
// Exec runs the resolve dockerfile command | ||
func (c *ResolveDockerfileCommand) Exec(ctx context.Context, args []string) error { | ||
if len(args) != 1 { | ||
return flag.ErrHelp | ||
} | ||
|
||
dockerfile, err := os.Open(args[0]) | ||
if err != nil { | ||
return fmt.Errorf("could not open Dockerfile: %w", err) | ||
} | ||
defer dockerfile.Close() | ||
|
||
resolvedDockerfile, err := resolveDigest(dockerfile) | ||
if err != nil { | ||
return fmt.Errorf("failed extracting images from Dockerfile: %w", err) | ||
} | ||
|
||
fmt.Fprintln(os.Stderr, string(resolvedDockerfile)) | ||
|
||
return nil | ||
} | ||
|
||
func resolveDigest(dockerfile io.Reader) ([]byte, error) { | ||
fileScanner := bufio.NewScanner(dockerfile) | ||
tmp := bytes.NewBuffer([]byte{}) | ||
|
||
for fileScanner.Scan() { | ||
line := strings.TrimSpace(fileScanner.Text()) | ||
|
||
// TODO(developer-guy): support the COPY --from=image:tag cases | ||
if strings.HasPrefix(strings.ToUpper(line), "FROM") { | ||
switch image := getImageFromLine(line); image { | ||
case "scratch": | ||
tmp.WriteString(line) | ||
default: | ||
ref, err := name.ParseReference(image) | ||
if err != nil { | ||
// we should not return err here since | ||
// we can define the image refs smth like | ||
// i.e., FROM alpine:$(TAG), FROM $(IMAGE), etc. | ||
tmp.WriteString(line) | ||
tmp.WriteString("\n") | ||
continue | ||
} | ||
|
||
d, err := remote.ResolveDigest(ref) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "resolving digest") | ||
} | ||
|
||
// rewrite the image as follows: | ||
// alpine:3.13 => index.docker.io/library/alpine@sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c | ||
tmp.WriteString(strings.ReplaceAll(line, image, d.String())) | ||
} | ||
} else { | ||
tmp.WriteString(line) | ||
} | ||
tmp.WriteString("\n") | ||
} | ||
|
||
return tmp.Bytes(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package dockerfile | ||
|
||
import ( | ||
"bytes" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func Test_resolveDigest(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
dockerfile string | ||
want string | ||
wantErr bool | ||
}{ | ||
{ | ||
"happy alpine", | ||
`FROM alpine:3.13`, | ||
`FROM index.docker.io/library/alpine@sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c | ||
`, | ||
false, | ||
}, | ||
{ | ||
"alpine with digest", | ||
`FROM alpine@sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c`, | ||
`FROM alpine@sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c | ||
`, | ||
false, | ||
}, | ||
{ | ||
"multi-line", | ||
`FROM alpine:3.13 | ||
COPY . . | ||
RUN ls`, | ||
`FROM index.docker.io/library/alpine@sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c | ||
COPY . . | ||
RUN ls | ||
`, | ||
false, | ||
}, | ||
{ | ||
"skip scratch", | ||
`FROM alpine:3.13 | ||
FROM scratch | ||
RUN ls`, | ||
`FROM index.docker.io/library/alpine@sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c | ||
FROM scratch | ||
RUN ls | ||
`, | ||
false, | ||
}, | ||
{ | ||
"should not break invalid image ref", | ||
`FROM alpine:$(TAG) | ||
FROM $(IMAGE) | ||
`, | ||
`FROM alpine:$(TAG) | ||
FROM $(IMAGE) | ||
`, | ||
false, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got, err := resolveDigest(bytes.NewBuffer([]byte(tt.dockerfile))) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("resolveDigest() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if !reflect.DeepEqual(string(got), tt.want) { | ||
t.Errorf("resolveDigest() got = %v, want %v", string(got), tt.want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package options | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
// ResolveDockerfileOptions is the top level wrapper for the `verify blob` command. | ||
type ResolveDockerfileOptions struct { | ||
Output string | ||
} | ||
|
||
var _ Interface = (*ResolveDockerfileOptions)(nil) | ||
|
||
// AddFlags implements Interface | ||
func (o *ResolveDockerfileOptions) AddFlags(cmd *cobra.Command) { | ||
cmd.Flags().StringVar(&o.Output, "output", "", | ||
"output an updated Dockerfile to file") | ||
} |