diff --git a/go.mod b/go.mod index 83b092c..23ca992 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,11 @@ require ( ) require ( + github.com/agext/levenshtein v1.2.3 // indirect github.com/containerd/typeurl v1.0.2 // indirect + github.com/docker/docker v20.10.7+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.4.0 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect diff --git a/go.sum b/go.sum index 0c1e6af..dbf1502 100644 --- a/go.sum +++ b/go.sum @@ -144,6 +144,7 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -371,14 +372,17 @@ github.com/docker/docker v17.12.0-ce-rc1.0.20200730172259-9f28837c1d93+incompati github.com/docker/docker v20.10.0-beta1.0.20201110211921-af34b94a78a1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.3-0.20210609071616-4c2ec79bf2a8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ= github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libnetwork v0.8.0-dev.2.0.20200917202933-d0951081b35f/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= diff --git a/internal/parser/dockerfile/dockerfile.go b/internal/parser/dockerfile/dockerfile.go index 9e6ccc3..82652f8 100644 --- a/internal/parser/dockerfile/dockerfile.go +++ b/internal/parser/dockerfile/dockerfile.go @@ -18,11 +18,15 @@ limitations under the License. package dockerfile import ( + "errors" + "fmt" "os" + "strings" log "github.com/sirupsen/logrus" - buildkitDockerfile "github.com/moby/buildkit/frontend/dockerfile/parser" + buildkitDockerfileInstructions "github.com/moby/buildkit/frontend/dockerfile/instructions" + buildkitDockerfileParser "github.com/moby/buildkit/frontend/dockerfile/parser" "sothr.com/warricksothr/pinned-package-updater/internal/parser" ) @@ -41,11 +45,91 @@ func Parse(dockerfilePath string) (parser.ParseResult, error) { } }(dockerfile) - _, err = buildkitDockerfile.Parse(dockerfile) + dockerfileParseResults, err := buildkitDockerfileParser.Parse(dockerfile) if err != nil { log.Fatalf("Unexpected error parsing Dockerfile %s: %s", dockerfilePath, err) return parser.ParseResult{}, err } - return parser.ParseResult{}, nil + dockerfileParseResult, err := parseDockerfileAST(dockerfileParseResults.AST) + if err != nil { + log.Fatalf("Unexpected error while parsing AST for %s: %s", dockerfilePath, err) + return parser.ParseResult{}, err + } + + return dockerfileParseResult, nil +} + +type ASTStage struct { + children []*buildkitDockerfileParser.Node +} + +// parseDockerfileAST Read the parsed AST and extract the required information from each stage +func parseDockerfileAST(dockerfileAST *buildkitDockerfileParser.Node) (parser.ParseResult, error) { + + dockerfileStages, _, err := buildkitDockerfileInstructions.Parse(dockerfileAST) + if err != nil { + log.Fatal("Unexpected error while parsing Dockerfile Instructions") + return parser.ParseResult{}, err + } + + var stages []*parser.Stage + for stageIndex, stage := range dockerfileStages { + stage, err := parseDockerfileStage(stage) + if err != nil { + log.Fatal("Unexpected error while parsing stage %d", stageIndex) + return parser.ParseResult{}, err + } + stages = append(stages, &stage) + } + + return parser.ParseResult{Stages: stages}, nil +} + +func parseDockerfileStage(dockerfileStage buildkitDockerfileInstructions.Stage) (parser.Stage, error) { + + imageParts := strings.Split(dockerfileStage.BaseName, ":") + + if len(imageParts) < 2 { + message := fmt.Sprintf("Not enough information in image (%s) to determine base", imageParts) + log.Fatal(message) + return parser.Stage{}, errors.New(message) + } + + name := dockerfileStage.Name + image := parser.Image{ + Name: imageParts[0], + Tag: imageParts[1], + } + + repositories, err := parseRepositoriesFromDockerfileStage(dockerfileStage) + if err != nil { + message := fmt.Sprintf("Unexpected error while parsing repositories from stage") + log.Fatal(message) + return parser.Stage{}, errors.New(message) + } + + packages, err := parsePackagesFromDockerfileStage(dockerfileStage) + if err != nil { + message := fmt.Sprintf("Unexpected error while parsing packages from stage") + log.Fatal(message) + return parser.Stage{}, errors.New(message) + } + + return parser.Stage{ + Name: name, + Image: &image, + Repositories: repositories, + Packages: packages, + }, nil +} + +func parsePackagesFromDockerfileStage(_ buildkitDockerfileInstructions.Stage) ([]*parser.Package, error) { + var packages []*parser.Package + return packages, nil +} + +func parseRepositoriesFromDockerfileStage(_ buildkitDockerfileInstructions.Stage) ([]*parser.Repository, error) { + var repositories []*parser.Repository + return repositories, nil } diff --git a/internal/parser/parser.go b/internal/parser/parser.go index 2a699a8..b0ffc29 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -19,24 +19,30 @@ package parser // ParseResult is the standard representation for am image declaration type ParseResult struct { - stages []Stage + Stages []*Stage } // Stage is the representation of a stage in the image declaration type Stage struct { - name string - image string - packages []Package - repositories []Repository + Name string + Image *Image + Repositories []*Repository + Packages []*Package +} + +// Image is the representation of the reference to a container image layer +type Image struct { + Name string + Tag string } // Package is the representation of an installed pinned package defined in an image stage declaration type Package struct { - name string - version string + Name string + Version string } // Repository is the representation of an added repository defined in a stage declaration type Repository struct { - path string + Path string } diff --git a/test/data/old_pinned_dependencies_ubuntu.Dockerfile b/test/data/old_pinned_dependencies_ubuntu.Dockerfile new file mode 100644 index 0000000..23abb03 --- /dev/null +++ b/test/data/old_pinned_dependencies_ubuntu.Dockerfile @@ -0,0 +1,7 @@ +FROM ubuntu:18.04 + +RUN apt-get update && apt-get install \ + ca-certificates=20210119~18.04.1 \ + curl=7.58.0-2ubuntu3.15 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ No newline at end of file