Browse Source

Code cleanup

master
Drew Short 3 years ago
parent
commit
6d4455c343
  1. 182
      internal/parser/debian.go
  2. 106
      internal/parser/dockerfile/dockerfile.go
  3. 8
      internal/parser/parser.go

182
internal/parser/debian.go

@ -32,6 +32,94 @@ type DebianCommandParser struct {
printer *syntax.Printer
}
// 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, scriptParseErr := c.parser.Parse(reader, "")
if scriptParseErr != nil {
log.Fatalf("Failed to parse script \"%s\": %s", command, scriptParseErr)
return nil, scriptParseErr
}
var hasErrorWhileWalkingScript bool = false
var errorWhileWalkingScript error
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, repositoryParseErr := c.getAddedRepository(*parsedCommand, parsedStatement.Redirs)
if repositoryParseErr != nil {
log.Fatalf("Unexpected error while parsing repository from \"%s\": %s", c.shellNodeToString(node), repositoryParseErr)
hasErrorWhileWalkingScript = true
errorWhileWalkingScript = repositoryParseErr
// Terminate walking early so we can raise an error
return false
}
repositories = append(repositories, &Repository{Information: repositoryInformation})
log.Debugf("Found repository \"%s\"", repositoryInformation)
}
}
}
return true
})
if hasErrorWhileWalkingScript {
return repositories, errorWhileWalkingScript
}
return repositories, nil
}
// GetPinnedPackages get a list of all pinned packages
func (c DebianCommandParser) GetPinnedPackages(command string) ([]*Package, error) {
var packages []*Package
reader := strings.NewReader(command)
parsedScript, scriptParseErr := c.parser.Parse(reader, "")
if scriptParseErr != nil {
log.Fatalf("Failed to parse script \"%s\"", command)
return nil, scriptParseErr
}
var hasErrorWhileWalkingScript bool = false
var errorWhileWalkingScript error
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, pinnedPackageParseErr := c.getInstalledPinnedPackages(*parsedCommand)
if pinnedPackageParseErr != nil {
log.Fatalf("Unexpected error while parsing pinned packages from \"%s\": %s", c.shellNodeToString(node), pinnedPackageParseErr)
hasErrorWhileWalkingScript = true
errorWhileWalkingScript = pinnedPackageParseErr
// Terminate walking early so we can raise an error
return false
}
for _, pinnedPackage := range pinnedPackages {
packages = append(packages, pinnedPackage)
log.Debugf("Found pinned package \"%s=%s\"", pinnedPackage.Name, pinnedPackage.Version)
}
}
}
}
return true
})
if hasErrorWhileWalkingScript {
return packages, errorWhileWalkingScript
}
return packages, nil
}
/*
General Helpers
*/
// newDebianCommandParser Provision a new instance of DebianCommandParser with required components
func newDebianCommandParser() DebianCommandParser {
return DebianCommandParser{
@ -50,6 +138,20 @@ func (c DebianCommandParser) shellNodeToString(node syntax.Node) string {
return buffer.String()
}
// trimQuotes return a string without surrounding matching 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
}
/*
Repository Helpers
*/
// 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 {
@ -74,16 +176,6 @@ func isAddRepositoryExpression(command syntax.CallExpr, redirects []*syntax.Redi
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
@ -102,38 +194,6 @@ func (c DebianCommandParser) getAddedRepository(command syntax.CallExpr, redirec
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
@ -144,6 +204,10 @@ func isInstallerExpression(command syntax.CallExpr) bool {
return false
}
/*
Pinned Package Helpers
*/
func (c DebianCommandParser) getInstalledPinnedPackages(command syntax.CallExpr) ([]*Package, error) {
var packages []*Package
@ -165,37 +229,3 @@ func (c DebianCommandParser) getInstalledPinnedPackages(command syntax.CallExpr)
}
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
}

106
internal/parser/dockerfile/dockerfile.go

@ -25,60 +25,56 @@ import (
log "github.com/sirupsen/logrus"
buildkitDockerfileInstructions "github.com/moby/buildkit/frontend/dockerfile/instructions"
buildkitDockerfileParser "github.com/moby/buildkit/frontend/dockerfile/parser"
buildkitInstructions "github.com/moby/buildkit/frontend/dockerfile/instructions"
buildkitParser "github.com/moby/buildkit/frontend/dockerfile/parser"
"sothr.com/warricksothr/pinned-package-updater/internal/parser"
)
// Parse reads a Dockerfile, interprets the AST and returns information about the image(s) and package(s) defined
func Parse(dockerfilePath string) (parser.ParseResult, error) {
dockerfile, err := os.Open(dockerfilePath)
if err != nil {
log.Fatalf("Unexpected error reading file %s: %s", dockerfilePath, err)
return parser.ParseResult{}, err
dockerfile, dockerfileOpenErr := os.Open(dockerfilePath)
if dockerfileOpenErr != nil {
log.Fatalf("Unexpected error reading file %s: %s", dockerfilePath, dockerfileOpenErr)
return parser.ParseResult{}, dockerfileOpenErr
}
defer func(dockerfile *os.File) {
err := dockerfile.Close()
if err != nil {
log.Error("Unexpected error closing file %s: %s", dockerfilePath, err)
dockerfileCloseErr := dockerfile.Close()
if dockerfileCloseErr != nil {
log.Error("Unexpected error closing file %s: %s", dockerfilePath, dockerfileCloseErr)
}
}(dockerfile)
dockerfileParseResults, err := buildkitDockerfileParser.Parse(dockerfile)
if err != nil {
log.Fatalf("Unexpected error parsing Dockerfile %s: %s", dockerfilePath, err)
return parser.ParseResult{}, err
parsedASTResult, ASTParseErr := buildkitParser.Parse(dockerfile)
if ASTParseErr != nil {
log.Fatalf("Unexpected error parsing Dockerfile %s: %s", dockerfilePath, ASTParseErr)
return parser.ParseResult{}, ASTParseErr
}
dockerfileParseResult, err := parseDockerfileAST(dockerfileParseResults.AST)
if err != nil {
log.Fatalf("Unexpected error while parsing AST for %s: %s", dockerfilePath, err)
return parser.ParseResult{}, err
parseResult, parseErr := parseDockerfileAST(parsedASTResult.AST)
if parseErr != nil {
log.Fatalf("Unexpected error while parsing AST for %s: %s", dockerfilePath, parseErr)
return parser.ParseResult{}, parseErr
}
return dockerfileParseResult, nil
}
type ASTStage struct {
children []*buildkitDockerfileParser.Node
return parseResult, nil
}
// parseDockerfileAST Read the parsed AST and extract the required information from each stage
func parseDockerfileAST(dockerfileAST *buildkitDockerfileParser.Node) (parser.ParseResult, error) {
func parseDockerfileAST(dockerfileAST *buildkitParser.Node) (parser.ParseResult, error) {
dockerfileStages, _, err := buildkitDockerfileInstructions.Parse(dockerfileAST)
if err != nil {
log.Fatal("Unexpected error while parsing Dockerfile Instructions")
return parser.ParseResult{}, err
dockerfileStages, _, stageParsingErr := buildkitInstructions.Parse(dockerfileAST)
if stageParsingErr != nil {
log.Fatalf("Unexpected error while parsing Dockerfile Instructions: %s", stageParsingErr)
return parser.ParseResult{}, stageParsingErr
}
var stages []*parser.Stage
for stageIndex, stage := range dockerfileStages {
stage, err := parseDockerfileStage(stage)
if err != nil {
stage, stageErr := parseDockerfileStage(stage)
if stageErr != nil {
log.Fatal("Unexpected error while parsing stage %d", stageIndex)
return parser.ParseResult{}, err
return parser.ParseResult{}, stageErr
}
stages = append(stages, &stage)
}
@ -86,7 +82,7 @@ func parseDockerfileAST(dockerfileAST *buildkitDockerfileParser.Node) (parser.Pa
return parser.ParseResult{Stages: stages}, nil
}
func parseDockerfileStage(dockerfileStage buildkitDockerfileInstructions.Stage) (parser.Stage, error) {
func parseDockerfileStage(dockerfileStage buildkitInstructions.Stage) (parser.Stage, error) {
imageParts := strings.Split(dockerfileStage.BaseName, ":")
@ -102,22 +98,22 @@ func parseDockerfileStage(dockerfileStage buildkitDockerfileInstructions.Stage)
Tag: imageParts[1],
}
commandParser, err := parser.GetCommandParser(image)
if err != nil {
log.Fatalf("Unexpected error while determining command parser for image %s", image)
return parser.Stage{}, err
commandParser, commandParserRetrievalErr := parser.GetCommandParser(image)
if commandParserRetrievalErr != nil {
log.Fatalf("Unexpected error while determining command parser for image \"%s\": %s", image, commandParserRetrievalErr)
return parser.Stage{}, commandParserRetrievalErr
}
repositories, err := parseRepositoriesFromDockerfileStage(dockerfileStage, commandParser)
if err != nil {
message := fmt.Sprintf("Unexpected error while parsing repositories from stage")
repositories, repositoryCommandParseErr := parseRepositoriesFromDockerfileStage(dockerfileStage, commandParser)
if repositoryCommandParseErr != nil {
message := fmt.Sprintf("Unexpected error while parsing repositories from stage: %s", repositoryCommandParseErr)
log.Fatal(message)
return parser.Stage{}, errors.New(message)
}
packages, err := parsePackagesFromDockerfileStage(dockerfileStage, commandParser)
if err != nil {
message := fmt.Sprintf("Unexpected error while parsing packages from stage")
packages, packageCommandParseErr := parsePackagesFromDockerfileStage(dockerfileStage, commandParser)
if packageCommandParseErr != nil {
message := fmt.Sprintf("Unexpected error while parsing packages from stage: %s", packageCommandParseErr)
log.Fatal(message)
return parser.Stage{}, errors.New(message)
}
@ -130,21 +126,21 @@ func parseDockerfileStage(dockerfileStage buildkitDockerfileInstructions.Stage)
}, nil
}
func parseRepositoriesFromDockerfileStage(dockerfileStage buildkitDockerfileInstructions.Stage, commandParser *parser.CommandParser) ([]*parser.Repository, error) {
func parseRepositoriesFromDockerfileStage(dockerfileStage buildkitInstructions.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)
case *buildkitInstructions.RunCommand:
runCommand := command.(*buildkitInstructions.RunCommand)
log.Tracef("Parsing RunCommand \"%s\" for repositories", runCommand.CmdLine)
parsedRepositories, err := typedCommandParser.GetRepositories(runCommand.CmdLine[0])
if err != nil {
log.Fatalf("Unexpected error while parsing repositories: %s", err)
return nil, err
parsedRepositories, repositoryParseErr := typedCommandParser.GetRepositories(runCommand.CmdLine[0])
if repositoryParseErr != nil {
log.Fatalf("Unexpected error while parsing repositories: %s", repositoryParseErr)
return nil, repositoryParseErr
}
for _, parsedRepository := range parsedRepositories {
repositories = append(repositories, parsedRepository)
@ -155,21 +151,21 @@ func parseRepositoriesFromDockerfileStage(dockerfileStage buildkitDockerfileInst
return repositories, nil
}
func parsePackagesFromDockerfileStage(dockerfileStage buildkitDockerfileInstructions.Stage, commandParser *parser.CommandParser) ([]*parser.Package, error) {
func parsePackagesFromDockerfileStage(dockerfileStage buildkitInstructions.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)
case *buildkitInstructions.RunCommand:
runCommand := command.(*buildkitInstructions.RunCommand)
log.Tracef("Parsing RunCommand \"%s\" for packages", runCommand.CmdLine)
parsedPinnedPackages, err := typedCommandParser.GetPinnedPackages(runCommand.CmdLine[0])
if err != nil {
log.Fatalf("Unexpected error while parsing pinned packages: %s", err)
return nil, err
parsedPinnedPackages, packageParseErr := typedCommandParser.GetPinnedPackages(runCommand.CmdLine[0])
if packageParseErr != nil {
log.Fatalf("Unexpected error while parsing pinned packages: %s", packageParseErr)
return nil, packageParseErr
}
for _, parsedPinnedPackage := range parsedPinnedPackages {
packages = append(packages, parsedPinnedPackage)

8
internal/parser/parser.go

@ -92,10 +92,10 @@ func InitCommandParsers() error {
}
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
parserType, determineParserErr := determineParserForImage(image)
if determineParserErr != nil {
log.Fatalf("Failed to determine a matching CommandParser for %s: %s", image, determineParserErr)
return nil, determineParserErr
}
if requestedCommandParser, ok := availableCommandParsers[parserType]; ok {

Loading…
Cancel
Save