Browse Source

Added basic Debian repository and package parsing

master
Drew Short 3 years ago
parent
commit
ad101211f9
  1. 12
      cmd/check.go
  2. 3
      go.mod
  3. 18
      go.sum
  4. 201
      internal/parser/debian.go
  5. 54
      internal/parser/dockerfile/dockerfile.go
  6. 70
      internal/parser/parser.go
  7. 5
      test/data/old_pinned_dependencies_ubuntu.Dockerfile

12
cmd/check.go

@ -17,12 +17,14 @@ limitations under the License.
package cmd
import (
log "github.com/sirupsen/logrus"
"os"
"path"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"sothr.com/warricksothr/pinned-package-updater/internal/parser"
"sothr.com/warricksothr/pinned-package-updater/internal/parser/dockerfile"
)
@ -69,6 +71,14 @@ pinned-package-updater --remote <address of the upstream service> check [Dockerf
func init() {
rootCmd.AddCommand(checkCmd)
// TODO: Add dynamic loading of maps between images and command parsers
err := parser.InitCommandParsers()
if err != nil {
log.Errorf("Unexpected error while ")
return
}
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command

3
go.mod

@ -8,6 +8,7 @@ require (
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1
github.com/thediveo/enumflag v0.10.1
mvdan.cc/sh/v3 v3.4.0
)
require (
@ -30,7 +31,7 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665 // indirect
golang.org/x/text v0.3.5 // indirect
google.golang.org/protobuf v1.26.0 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect

18
go.sum

@ -338,6 +338,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
@ -414,6 +415,8 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.13.1 h1:xVm/f9seEhZFL9+n5kv5XLrGwy6elc4V9v/XFY2vmd8=
github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@ -585,6 +588,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
@ -726,6 +730,8 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
@ -890,6 +896,7 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -947,6 +954,9 @@ github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@ -1391,10 +1401,13 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665 h1:QOQNt6vCjMpXE7JSK5VvAzJC1byuN3FgTNSBwf+CJgI=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1744,8 +1757,11 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/sh/v3 v3.4.0 h1:thPCJ0hffn/Y/vMGs3HVMPYStNTyr2+lQee8LQiPZSU=
mvdan.cc/sh/v3 v3.4.0/go.mod h1:p/tqPPI4Epfk2rICAe2RoaNd8HBSJ8t9Y2DA9yQlbzY=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7/go.mod h1:HGC5lll35J70Y5v7vCGb9oLhHoScFwkHDJm/05RdSTc=
pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=

201
internal/parser/debian.go

@ -0,0 +1,201 @@
/*
Copyright © 2021 Drew Short <warrick@sothr.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package parser
import (
"bytes"
"errors"
"strings"
log "github.com/sirupsen/logrus"
"mvdan.cc/sh/v3/syntax"
)
// DebianCommandParser the parser responsible for handling Debian based images
type DebianCommandParser struct {
parser *syntax.Parser
printer *syntax.Printer
}
// newDebianCommandParser Provision a new instance of DebianCommandParser with required components
func newDebianCommandParser() DebianCommandParser {
return DebianCommandParser{
parser: syntax.NewParser(),
printer: syntax.NewPrinter(),
}
}
// shellNodeToString Convert a node to the string representation
func (c DebianCommandParser) shellNodeToString(node syntax.Node) string {
buffer := bytes.NewBufferString("")
err := c.printer.Print(buffer, node)
if err != nil {
log.Panic("Failed to convert node to string")
}
return buffer.String()
}
// isRedirectedToAptSourcesList Checks if the list of redirects contains the apt sources list directory
func isRedirectedToAptSourcesList(redirects []*syntax.Redirect) bool {
if redirects != nil && len(redirects) > 0 {
for _, redirect := range redirects {
if strings.Contains(redirect.Word.Lit(), "/etc/apt/sources.list") {
return true
}
}
}
return false
}
// isAddRepositoryExpression Checks for expressions that add to the repository
func isAddRepositoryExpression(command syntax.CallExpr, redirects []*syntax.Redirect) bool {
// Adding via debian tooling
if command.Args[0].Lit() == "add-apt-repository" {
return true
// Echoing into a sources file directly
} else if command.Args[0].Lit() == "echo" && isRedirectedToAptSourcesList(redirects) {
return true
}
return false
}
// trimQuotes return a string without surrounding quotes
func trimQuotes(value string) string {
if value[0] == '"' && value[len(value)-1] == '"' {
return value[1 : len(value)-1]
} else if value[0] == '\'' && value[len(value)-1] == '\'' {
return value[1 : len(value)-1]
}
return value
}
// getAddedRepository parse the repository information from the shell commands
func (c DebianCommandParser) getAddedRepository(command syntax.CallExpr, redirects []*syntax.Redirect) (string, error) {
// Adding via debian tooling
if command.Args[0].Lit() == "add-apt-repository" {
return command.Args[len(command.Args)-1].Lit(), nil
// Echoing into a sources file directly
} else if command.Args[0].Lit() == "echo" && isRedirectedToAptSourcesList(redirects) {
for _, argument := range command.Args {
var argumentNode syntax.Node = argument
argumentValue := trimQuotes(c.shellNodeToString(argumentNode))
if strings.Contains(argumentValue, "deb") || strings.Contains(argumentValue, "http://") {
return argumentValue, nil
}
}
}
return "", errors.New("failed to parse repository")
}
// GetRepositories Returns a list of Repository entries that were identified
func (c DebianCommandParser) GetRepositories(command string) ([]*Repository, error) {
var repositories []*Repository
reader := strings.NewReader(command)
parsedScript, err := c.parser.Parse(reader, "")
if err != nil {
log.Fatalf("Failed to parse script \"%s\"", command)
return nil, err
}
syntax.Walk(parsedScript, func(node syntax.Node) bool {
switch parsedStatement := node.(type) {
case *syntax.Stmt:
switch parsedCommand := parsedStatement.Cmd.(type) {
case *syntax.CallExpr:
if isAddRepositoryExpression(*parsedCommand, parsedStatement.Redirs) {
repositoryInformation, err := c.getAddedRepository(*parsedCommand, parsedStatement.Redirs)
if err != nil {
log.Fatalf("Unexpected error while parsing repository from \"%s\": %s", c.shellNodeToString(node), err)
}
repositories = append(repositories, &Repository{Information: repositoryInformation})
log.Debugf("Found repository \"%s\"", repositoryInformation)
}
}
}
return true
})
return repositories, nil
}
// isAddRepositoryExpression Checks for expressions that add to the repository
func isInstallerExpression(command syntax.CallExpr) bool {
// Adding via debian tooling
commandProgram := command.Args[0].Lit()
if (commandProgram == "apt-get" || commandProgram == "apt") && len(command.Args) > 1 && command.Args[1].Lit() == "install" {
return true
}
return false
}
func (c DebianCommandParser) getInstalledPinnedPackages(command syntax.CallExpr) ([]*Package, error) {
var packages []*Package
// Adding via debian tooling
commandProgram := command.Args[0].Lit()
if (commandProgram == "apt-get" || commandProgram == "apt") && len(command.Args) > 1 && command.Args[1].Lit() == "install" {
for _, argument := range command.Args {
var argumentNode syntax.Node = argument
argumentValue := trimQuotes(c.shellNodeToString(argumentNode))
if strings.Contains(argumentValue, "=") {
pinnedPackageParts := strings.SplitN(argumentValue, "=", 2)
packages = append(packages, &Package{
Name: pinnedPackageParts[0],
Version: pinnedPackageParts[1],
})
}
}
return packages, nil
}
return packages, errors.New("failed to parse packages")
}
// GetPinnedPackages get a list of all pinned packages
func (c DebianCommandParser) GetPinnedPackages(command string) ([]*Package, error) {
var packages []*Package
reader := strings.NewReader(command)
parsedScript, err := c.parser.Parse(reader, "")
if err != nil {
log.Fatalf("Failed to parse script \"%s\"", command)
return nil, err
}
syntax.Walk(parsedScript, func(node syntax.Node) bool {
switch parsedStatement := node.(type) {
case *syntax.Stmt:
switch parsedCommand := parsedStatement.Cmd.(type) {
case *syntax.CallExpr:
if isInstallerExpression(*parsedCommand) {
pinnedPackages, err := c.getInstalledPinnedPackages(*parsedCommand)
if err != nil {
log.Fatalf("Unexpected error while parsing pinned packages from \"%s\": %s", c.shellNodeToString(node), err)
}
for _, pinnedPackage := range pinnedPackages {
packages = append(packages, pinnedPackage)
log.Debugf("Found pinned package \"%s=%s\"", pinnedPackage.Name, pinnedPackage.Version)
}
}
}
}
return true
})
return packages, nil
}

54
internal/parser/dockerfile/dockerfile.go

@ -102,14 +102,20 @@ func parseDockerfileStage(dockerfileStage buildkitDockerfileInstructions.Stage)
Tag: imageParts[1],
}
repositories, err := parseRepositoriesFromDockerfileStage(dockerfileStage)
commandParser, err := parser.GetCommandParser(image)
if err != nil {
log.Fatalf("Unexpected error while determining command parser for image %s", image)
return parser.Stage{}, err
}
repositories, err := parseRepositoriesFromDockerfileStage(dockerfileStage, commandParser)
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)
packages, err := parsePackagesFromDockerfileStage(dockerfileStage, commandParser)
if err != nil {
message := fmt.Sprintf("Unexpected error while parsing packages from stage")
log.Fatal(message)
@ -124,12 +130,44 @@ func parseDockerfileStage(dockerfileStage buildkitDockerfileInstructions.Stage)
}, nil
}
func parsePackagesFromDockerfileStage(_ buildkitDockerfileInstructions.Stage) ([]*parser.Package, error) {
var packages []*parser.Package
return packages, nil
}
func parseRepositoriesFromDockerfileStage(_ buildkitDockerfileInstructions.Stage) ([]*parser.Repository, error) {
func parseRepositoriesFromDockerfileStage(dockerfileStage buildkitDockerfileInstructions.Stage, commandParser *parser.CommandParser) ([]*parser.Repository, error) {
var repositories []*parser.Repository
typedCommandParser := *commandParser
for _, command := range dockerfileStage.Commands {
switch command.(type) {
case *buildkitDockerfileInstructions.RunCommand:
runCommand := command.(*buildkitDockerfileInstructions.RunCommand)
log.Tracef("Parsing RunCommand \"%s\" for repositories", runCommand.CmdLine)
_, err := typedCommandParser.GetRepositories(runCommand.CmdLine[0])
if err != nil {
return nil, err
}
}
}
return repositories, nil
}
func parsePackagesFromDockerfileStage(dockerfileStage buildkitDockerfileInstructions.Stage, commandParser *parser.CommandParser) ([]*parser.Package, error) {
var packages []*parser.Package
typedCommandParser := *commandParser
for _, command := range dockerfileStage.Commands {
switch command.(type) {
case *buildkitDockerfileInstructions.RunCommand:
runCommand := command.(*buildkitDockerfileInstructions.RunCommand)
log.Tracef("Parsing RunCommand \"%s\" for packages", runCommand.CmdLine)
_, err := typedCommandParser.GetPinnedPackages(runCommand.CmdLine[0])
if err != nil {
return nil, err
}
}
}
return packages, nil
}

70
internal/parser/parser.go

@ -17,6 +17,13 @@ limitations under the License.
// Package parser implements the required components for working with image definitions
package parser
import (
"errors"
"fmt"
log "github.com/sirupsen/logrus"
)
// ParseResult is the standard representation for am image declaration
type ParseResult struct {
Stages []*Stage
@ -44,5 +51,66 @@ type Package struct {
// Repository is the representation of an added repository defined in a stage declaration
type Repository struct {
Path string
Information string
}
// CommandParserType represents the recognized type of parser usable for a specific image
type CommandParserType int
const (
CommandParserTypeDebian CommandParserType = iota
)
// CommandParserTypeIds is a lookup map between CommandParserType and a representation string
var commandParserTypeIds = map[CommandParserType][]string{
CommandParserTypeDebian: {"debian"},
}
func lookupCommandParserTypeName(parserType CommandParserType) string {
if parserTypeName, ok := commandParserTypeIds[parserType]; ok {
return parserTypeName[0]
} else {
return "UNKNOWN"
}
}
var availableCommandParsers = map[CommandParserType]CommandParser{}
// CommandParser handles parsing commands from RUN arguments dependent on the base image type
type CommandParser interface {
GetRepositories(runCommand string) ([]*Repository, error)
GetPinnedPackages(runCommand string) ([]*Package, error)
}
// InitCommandParsers exposes functionality to configure the behavior of mapping images to a CommandParser
func InitCommandParsers() error {
// Registering known parsers
availableCommandParsers[CommandParserTypeDebian] = newDebianCommandParser()
return nil
}
func GetCommandParser(image Image) (*CommandParser, error) {
parserType, err := determineParserForImage(image)
if err != nil {
log.Fatalf("Failed to determine a matching CommandParser for %s: %s", image, err)
return nil, err
}
if requestedCommandParser, ok := availableCommandParsers[parserType]; ok {
return &requestedCommandParser, nil
} else {
message := fmt.Sprintf("Unable to identify parser for %s", lookupCommandParserTypeName(parserType))
log.Fatal(message)
return nil, errors.New(message)
}
}
// determineParserForImage matches image information to the correct CommandParserType
func determineParserForImage(_ Image) (CommandParserType, error) {
// TODO: Map image information to the list of command parsers supported
return CommandParserTypeDebian, nil
}

5
test/data/old_pinned_dependencies_ubuntu.Dockerfile

@ -1,7 +1,8 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get install \
RUN echo "deb http://archive.canonical.com/ubuntu bionic partner" > /etc/apt/sources.list.d/partner.list \
&& 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/*
&& rm -rf /var/lib/apt/lists/* \
Loading…
Cancel
Save