Browse Source

Improved logging and standardized logging pattern

master
Drew Short 3 years ago
parent
commit
15facca23c
  1. 17
      cmd/check.go
  2. 17
      cmd/root.go
  3. 74
      internal/parser/debian.go
  4. 249
      internal/parser/dockerfile/dockerfile.go
  5. 108
      internal/parser/parser.go

17
cmd/check.go

@ -54,14 +54,23 @@ pinned-package-updater --remote <address of the upstream service> check [Dockerf
cobra.CheckErr(currentDirectoryErr) cobra.CheckErr(currentDirectoryErr)
defaultDockerfilePath := path.Join(currentDirectory, "Dockerfile") defaultDockerfilePath := path.Join(currentDirectory, "Dockerfile")
args = append(args, defaultDockerfilePath) args = append(args, defaultDockerfilePath)
log.Debugf("No dockerfile path provided, attempting to use the default %s dockerfile", defaultDockerfilePath)
log.WithFields(log.Fields{
"defaultFilePath": defaultDockerfilePath,
}).Debug("no dockerfile path provided, attempting to use the default dockerfile path")
} }
// Aggregate the parsed updates // Aggregate the parsed updates
for _, dockerfilePath := range args {
log.Debugf("Parsing %s", dockerfilePath)
numberOfFiles := len(args)
for i, dockerfilePath := range args {
log.WithFields(log.Fields{
"filePath": dockerfilePath,
}).Debugf("parsing docker file %d/%d", i+1, numberOfFiles)
_, err := dockerfile.Parse(dockerfilePath) _, err := dockerfile.Parse(dockerfilePath)
if err != nil { if err != nil {
log.WithFields(log.Fields{
"filePath": dockerfilePath,
"sourceError": err,
}).Fatal("unexpected error while parsing dockerfile")
return return
} }
} }
@ -77,7 +86,7 @@ func init() {
err := parser.InitCommandParsers() err := parser.InitCommandParsers()
if err != nil { if err != nil {
log.Errorf("Unexpected error while ")
log.Fatal("unexpected error while initializing command parsers")
return return
} }

17
cmd/root.go

@ -31,14 +31,14 @@ import (
type LogFormat enumflag.Flag type LogFormat enumflag.Flag
const ( const (
LoggingLogFmt LogFormat = iota
LoggingText
LoggingText LogFormat = iota
LoggingLogFmt
LoggingJson LoggingJson
) )
var LogFormatIds = map[LogFormat][]string{ var LogFormatIds = map[LogFormat][]string{
LoggingLogFmt: {"logfmt"},
LoggingText: {"text"}, LoggingText: {"text"},
LoggingLogFmt: {"logfmt"},
LoggingJson: {"json"}, LoggingJson: {"json"},
} }
@ -137,19 +137,22 @@ func initLogging() {
} }
switch logFormat { switch logFormat {
case LoggingText:
log.SetFormatter(&log.TextFormatter{})
case LoggingLogFmt: case LoggingLogFmt:
log.SetFormatter(&log.TextFormatter{ log.SetFormatter(&log.TextFormatter{
DisableColors: true, DisableColors: true,
FullTimestamp: true, FullTimestamp: true,
}) })
case LoggingText:
log.SetFormatter(&log.TextFormatter{})
case LoggingJson: case LoggingJson:
log.SetFormatter(&log.JSONFormatter{}) log.SetFormatter(&log.JSONFormatter{})
default: default:
log.SetFormatter(&log.TextFormatter{}) log.SetFormatter(&log.TextFormatter{})
} }
log.Debugf("Set the logging level to %s", log.GetLevel().String())
log.Debugf("Set the logging formatter to '%s'", LogFormatIds[logFormat][0])
log.WithFields(log.Fields{
"loggingFormatter": LogFormatIds[logFormat][0],
"loggingLevel": log.GetLevel().String(),
"loggingVerbosity": logVerbosity,
}).Info("configured logging")
} }

74
internal/parser/debian.go

@ -33,17 +33,19 @@ type DebianCommandParser struct {
} }
// GetRepositories Returns a list of Repository entries that were identified // GetRepositories Returns a list of Repository entries that were identified
func (c DebianCommandParser) GetRepositories(command string) ([]*Repository, error) {
func (c DebianCommandParser) GetRepositories(logger *log.Entry, command string) ([]*Repository, error) {
var repositories []*Repository var repositories []*Repository
reader := strings.NewReader(command) 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
parsedScript, err := c.parser.Parse(reader, "")
if err != nil {
logger.WithFields(log.Fields{
"sourceError": err,
}).Fatal("failed to parse script")
return nil, err
} }
var hasErrorWhileWalkingScript bool = false
var erroredWhileWalkingScript = false
var errorWhileWalkingScript error var errorWhileWalkingScript error
syntax.Walk(parsedScript, func(node syntax.Node) bool { syntax.Walk(parsedScript, func(node syntax.Node) bool {
switch parsedStatement := node.(type) { switch parsedStatement := node.(type) {
@ -51,22 +53,27 @@ func (c DebianCommandParser) GetRepositories(command string) ([]*Repository, err
switch parsedCommand := parsedStatement.Cmd.(type) { switch parsedCommand := parsedStatement.Cmd.(type) {
case *syntax.CallExpr: case *syntax.CallExpr:
if isAddRepositoryExpression(*parsedCommand, parsedStatement.Redirs) { 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
repositoryInformation, err := c.getAddedRepository(*parsedCommand, parsedStatement.Redirs)
if err != nil {
logger.WithFields(log.Fields{
"rawScriptNode": c.shellNodeToString(node),
"sourceError": err,
}).Fatal("unexpected error while parsing repository information")
erroredWhileWalkingScript = true
errorWhileWalkingScript = err
// Terminate walking early so we can raise an error // Terminate walking early so we can raise an error
return false return false
} }
repositories = append(repositories, &Repository{Information: repositoryInformation}) repositories = append(repositories, &Repository{Information: repositoryInformation})
log.Debugf("Found repository \"%s\"", repositoryInformation)
logger.WithFields(log.Fields{
"repositoryInformation": repositoryInformation,
}).Debug("found repository")
} }
} }
} }
return true return true
}) })
if hasErrorWhileWalkingScript {
if erroredWhileWalkingScript {
return repositories, errorWhileWalkingScript return repositories, errorWhileWalkingScript
} }
@ -74,17 +81,19 @@ func (c DebianCommandParser) GetRepositories(command string) ([]*Repository, err
} }
// GetPinnedPackages get a list of all pinned packages // GetPinnedPackages get a list of all pinned packages
func (c DebianCommandParser) GetPinnedPackages(command string) ([]*Package, error) {
func (c DebianCommandParser) GetPinnedPackages(logger *log.Entry, command string) ([]*Package, error) {
var packages []*Package var packages []*Package
reader := strings.NewReader(command) reader := strings.NewReader(command)
parsedScript, scriptParseErr := c.parser.Parse(reader, "")
if scriptParseErr != nil {
log.Fatalf("Failed to parse script \"%s\"", command)
return nil, scriptParseErr
parsedScript, err := c.parser.Parse(reader, "")
if err != nil {
logger.WithFields(log.Fields{
"sourceError": err,
}).Fatal("failed to parse script")
return nil, err
} }
var hasErrorWhileWalkingScript bool = false
var erroredWhileWalkingScript = false
var errorWhileWalkingScript error var errorWhileWalkingScript error
syntax.Walk(parsedScript, func(node syntax.Node) bool { syntax.Walk(parsedScript, func(node syntax.Node) bool {
switch parsedStatement := node.(type) { switch parsedStatement := node.(type) {
@ -92,24 +101,29 @@ func (c DebianCommandParser) GetPinnedPackages(command string) ([]*Package, erro
switch parsedCommand := parsedStatement.Cmd.(type) { switch parsedCommand := parsedStatement.Cmd.(type) {
case *syntax.CallExpr: case *syntax.CallExpr:
if isInstallerExpression(*parsedCommand) { 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
pinnedPackages, err := c.getInstalledPinnedPackages(*parsedCommand)
if err != nil {
logger.WithFields(log.Fields{
"rawScriptNode": c.shellNodeToString(node),
"sourceError": err,
}).Fatal("unexpected error while parsing pinned packages")
erroredWhileWalkingScript = true
errorWhileWalkingScript = err
// Terminate walking early so we can raise an error // Terminate walking early so we can raise an error
return false return false
} }
for _, pinnedPackage := range pinnedPackages { for _, pinnedPackage := range pinnedPackages {
packages = append(packages, pinnedPackage) packages = append(packages, pinnedPackage)
log.Debugf("Found pinned package \"%s=%s\"", pinnedPackage.Name, pinnedPackage.Version)
logger.WithFields(log.Fields{
"pinnedPackageInformation": pinnedPackage,
}).Debug("found pinned package")
} }
} }
} }
} }
return true return true
}) })
if hasErrorWhileWalkingScript {
if erroredWhileWalkingScript {
return packages, errorWhileWalkingScript return packages, errorWhileWalkingScript
} }
@ -194,6 +208,10 @@ func (c DebianCommandParser) getAddedRepository(command syntax.CallExpr, redirec
return "", errors.New("failed to parse repository") return "", errors.New("failed to parse repository")
} }
/*
Pinned Package Helpers
*/
// isAddRepositoryExpression Checks for expressions that add to the repository // isAddRepositoryExpression Checks for expressions that add to the repository
func isInstallerExpression(command syntax.CallExpr) bool { func isInstallerExpression(command syntax.CallExpr) bool {
// Adding via debian tooling // Adding via debian tooling
@ -204,10 +222,6 @@ func isInstallerExpression(command syntax.CallExpr) bool {
return false return false
} }
/*
Pinned Package Helpers
*/
func (c DebianCommandParser) getInstalledPinnedPackages(command syntax.CallExpr) ([]*Package, error) { func (c DebianCommandParser) getInstalledPinnedPackages(command syntax.CallExpr) ([]*Package, error) {
var packages []*Package var packages []*Package

249
internal/parser/dockerfile/dockerfile.go

@ -19,7 +19,6 @@ package dockerfile
import ( import (
"errors" "errors"
"fmt"
"os" "os"
"strings" "strings"
@ -33,63 +32,118 @@ import (
// Parse reads a Dockerfile, interprets the AST and returns information about the image(s) and package(s) defined // 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) { func Parse(dockerfilePath string) (parser.ParseResult, error) {
dockerfile, dockerfileOpenErr := os.Open(dockerfilePath)
if dockerfileOpenErr != nil {
log.Fatalf("Unexpected error reading file %s: %s", dockerfilePath, dockerfileOpenErr)
return parser.ParseResult{}, dockerfileOpenErr
parseLogger := log.WithFields(log.Fields{
"filePath": dockerfilePath,
})
dockerfile, err := os.Open(dockerfilePath)
if err != nil {
log.WithFields(log.Fields{
"sourceError": err,
}).Fatal("unexpected error reading file")
return parser.ParseResult{}, err
} }
defer func(dockerfile *os.File) { defer func(dockerfile *os.File) {
dockerfileCloseErr := dockerfile.Close()
if dockerfileCloseErr != nil {
log.Error("Unexpected error closing file %s: %s", dockerfilePath, dockerfileCloseErr)
err := dockerfile.Close()
if err != nil {
parseLogger.WithFields(log.Fields{
"sourceError": err,
}).Error("unexpected error closing file")
} }
}(dockerfile) }(dockerfile)
parsedASTResult, ASTParseErr := buildkitParser.Parse(dockerfile)
if ASTParseErr != nil {
log.Fatalf("Unexpected error parsing Dockerfile %s: %s", dockerfilePath, ASTParseErr)
return parser.ParseResult{}, ASTParseErr
parsedASTResult, err := buildkitParser.Parse(dockerfile)
if err != nil {
parseLogger.WithFields(log.Fields{
"sourceError": err,
}).Fatal("unexpected error parsing Dockerfile")
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
parseResult, err := parseDockerfileAST(parseLogger, dockerfilePath, parsedASTResult.AST)
if err != nil {
parseLogger.WithFields(log.Fields{
"sourceError": err,
}).Fatal("unexpected error while parsing AST")
return parser.ParseResult{}, err
} }
return parseResult, nil return parseResult, nil
} }
// parseDockerfileAST Read the parsed AST and extract the required information from each stage
func parseDockerfileAST(dockerfileAST *buildkitParser.Node) (parser.ParseResult, error) {
/*
General Helpers
*/
// getCommandLineLocation parse commandLineLocation information into the structure we will persist
func getContentLocation(location []buildkitParser.Range) (parser.ContentLocation, error) {
commandLocationStart := location[0].Start
commandLocationEnd := location[len(location)-1].End
return parser.ContentLocation{
StartLine: commandLocationStart.Line,
StartCharacter: commandLocationStart.Character,
EndLine: commandLocationEnd.Line,
EndCharacter: commandLocationEnd.Character,
}, nil
}
/*
Parsing Helper
*/
dockerfileStages, _, stageParsingErr := buildkitInstructions.Parse(dockerfileAST)
if stageParsingErr != nil {
log.Fatalf("Unexpected error while parsing Dockerfile Instructions: %s", stageParsingErr)
return parser.ParseResult{}, stageParsingErr
// parseDockerfileAST Read the parsed AST and extract the required information from each stage
func parseDockerfileAST(logger *log.Entry, dockerFilePath string, dockerfileAST *buildkitParser.Node) (parser.ParseResult, error) {
dockerfileStages, _, err := buildkitInstructions.Parse(dockerfileAST)
if err != nil {
logger.WithFields(log.Fields{
"sourceError": err,
}).Fatal("unexpected error while parsing Dockerfile instructions")
return parser.ParseResult{}, err
} }
var stages []*parser.Stage var stages []*parser.Stage
for stageIndex, stage := range dockerfileStages { for stageIndex, stage := range dockerfileStages {
stage, stageErr := parseDockerfileStage(stage)
if stageErr != nil {
log.Fatal("Unexpected error while parsing stage %d", stageIndex)
return parser.ParseResult{}, stageErr
stageLogger := logger.WithFields(log.Fields{
"stageIndex": stageIndex,
"stageName": stage.Name,
})
stage, err := parseDockerfileStage(stageLogger, stageIndex, stage)
if err != nil {
stageLogger.WithFields(log.Fields{
"sourceError": err,
}).Fatal("unexpected error while parsing stage")
return parser.ParseResult{}, err
} }
stages = append(stages, &stage) stages = append(stages, &stage)
} }
return parser.ParseResult{Stages: stages}, nil
return parser.ParseResult{FilePath: dockerFilePath, Stages: stages}, nil
} }
func parseDockerfileStage(dockerfileStage buildkitInstructions.Stage) (parser.Stage, error) {
func parseDockerfileStage(logger *log.Entry, dockerfileStageIndex int, dockerfileStage buildkitInstructions.Stage) (parser.Stage, error) {
stageLocation, err := getContentLocation(dockerfileStage.Location)
if err != nil {
logger.WithFields(log.Fields{
"rawStageLocation": dockerfileStage.Location,
}).Fatal("unexpected failure parsing stage location information")
return parser.Stage{}, err
}
stageLogger := logger.WithFields(log.Fields{
"stageImage": dockerfileStage.BaseName,
"stageLocation": stageLocation,
})
imageParts := strings.Split(dockerfileStage.BaseName, ":") imageParts := strings.Split(dockerfileStage.BaseName, ":")
if len(imageParts) < 2 { 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)
stageLogger.Fatal("not enough information in image to determine base")
return parser.Stage{}, errors.New("not enough information in image to determine base")
} }
name := dockerfileStage.Name name := dockerfileStage.Name
@ -98,80 +152,125 @@ func parseDockerfileStage(dockerfileStage buildkitInstructions.Stage) (parser.St
Tag: imageParts[1], Tag: imageParts[1],
} }
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
commandParser, err := parser.GetCommandParser(image)
if err != nil {
stageLogger.WithFields(log.Fields{
"sourceError": err,
}).Fatal("unexpected error while determining command parser for image")
return parser.Stage{}, err
} }
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)
repositories, repositoryCommandLocations, err := parseRepositoriesFromDockerfileStage(stageLogger, dockerfileStage, commandParser)
if err != nil {
stageLogger.WithFields(log.Fields{
"sourceError": err,
}).Fatal("unexpected error while parsing repositories from stage")
return parser.Stage{}, err
} }
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)
packages, packageCommandLocations, err := parsePackagesFromDockerfileStage(stageLogger, dockerfileStage, commandParser)
if err != nil {
stageLogger.WithFields(log.Fields{
"sourceError": err,
}).Fatal("unexpected error while parsing packages from stage")
return parser.Stage{}, err
} }
var commandLocations []*parser.ContentLocation
commandLocations = append(commandLocations, repositoryCommandLocations...)
commandLocations = append(commandLocations, packageCommandLocations...)
return parser.Stage{ return parser.Stage{
Name: name,
Image: &image,
Repositories: repositories,
Packages: packages,
Index: dockerfileStageIndex,
Name: name,
Image: &image,
StageLocation: stageLocation,
Repositories: repositories,
Packages: packages,
CommandLocations: commandLocations,
}, nil }, nil
} }
func parseRepositoriesFromDockerfileStage(dockerfileStage buildkitInstructions.Stage, commandParser *parser.CommandParser) ([]*parser.Repository, error) {
func parseRepositoriesFromDockerfileStage(logger *log.Entry, dockerfileStage buildkitInstructions.Stage, commandParser *parser.CommandParser) ([]*parser.Repository, []*parser.ContentLocation, error) {
var repositories []*parser.Repository var repositories []*parser.Repository
var commandLocations []*parser.ContentLocation
typedCommandParser := *commandParser typedCommandParser := *commandParser
for _, command := range dockerfileStage.Commands { for _, command := range dockerfileStage.Commands {
switch command.(type) {
switch typedCommand := command.(type) {
case *buildkitInstructions.RunCommand: case *buildkitInstructions.RunCommand:
runCommand := command.(*buildkitInstructions.RunCommand)
log.Tracef("Parsing RunCommand \"%s\" for repositories", runCommand.CmdLine)
parsedRepositories, repositoryParseErr := typedCommandParser.GetRepositories(runCommand.CmdLine[0])
if repositoryParseErr != nil {
log.Fatalf("Unexpected error while parsing repositories: %s", repositoryParseErr)
return nil, repositoryParseErr
commandLocation, err := getContentLocation(typedCommand.Location())
if err != nil {
logger.WithFields(log.Fields{
"rawCommandLineLocation": typedCommand.Location(),
}).Fatal("unexpected failure parsing script location information")
return repositories, commandLocations, err
} }
for _, parsedRepository := range parsedRepositories {
repositories = append(repositories, parsedRepository)
commandLocations = append(commandLocations, &commandLocation)
for _, line := range typedCommand.CmdLine {
logger.WithFields(log.Fields{
"commandLine": line,
"commandLocation": commandLocation,
}).Trace("parsing RunCommand for repositories")
parsedRepositories, err := typedCommandParser.GetRepositories(logger, line)
if err != nil {
logger.WithFields(log.Fields{
"commandLine": line,
"commandLocation": commandLocation,
"sourceError": err,
}).Fatal("unexpected error while parsing repositories")
return repositories, commandLocations, err
}
for _, parsedRepository := range parsedRepositories {
repositories = append(repositories, parsedRepository)
}
} }
} }
} }
return repositories, nil
return repositories, commandLocations, nil
} }
func parsePackagesFromDockerfileStage(dockerfileStage buildkitInstructions.Stage, commandParser *parser.CommandParser) ([]*parser.Package, error) {
func parsePackagesFromDockerfileStage(logger *log.Entry, dockerfileStage buildkitInstructions.Stage, commandParser *parser.CommandParser) ([]*parser.Package, []*parser.ContentLocation, error) {
var packages []*parser.Package var packages []*parser.Package
var commandLocations []*parser.ContentLocation
typedCommandParser := *commandParser typedCommandParser := *commandParser
for _, command := range dockerfileStage.Commands { for _, command := range dockerfileStage.Commands {
switch command.(type) {
switch typedCommand := command.(type) {
case *buildkitInstructions.RunCommand: case *buildkitInstructions.RunCommand:
runCommand := command.(*buildkitInstructions.RunCommand)
log.Tracef("Parsing RunCommand \"%s\" for packages", runCommand.CmdLine)
parsedPinnedPackages, packageParseErr := typedCommandParser.GetPinnedPackages(runCommand.CmdLine[0])
if packageParseErr != nil {
log.Fatalf("Unexpected error while parsing pinned packages: %s", packageParseErr)
return nil, packageParseErr
commandLocation, err := getContentLocation(typedCommand.Location())
if err != nil {
logger.WithFields(log.Fields{
"rawCommandLineLocation": typedCommand.Location(),
}).Fatal("unexpected failure parsing script location information")
return packages, commandLocations, err
} }
for _, parsedPinnedPackage := range parsedPinnedPackages {
packages = append(packages, parsedPinnedPackage)
commandLocations = append(commandLocations, &commandLocation)
for _, line := range typedCommand.CmdLine {
logger.WithFields(log.Fields{
"commandLine": line,
"commandLocation": commandLocation,
}).Trace("parsing RunCommand for packages")
parsedPinnedPackages, err := typedCommandParser.GetPinnedPackages(logger, line)
if err != nil {
logger.WithFields(log.Fields{
"commandLine": line,
"commandLocation": commandLocation,
"sourceError": err,
}).Fatal("unexpected error while parsing pinned packages")
return packages, commandLocations, err
}
for _, parsedPinnedPackage := range parsedPinnedPackages {
packages = append(packages, parsedPinnedPackage)
}
} }
} }
} }
return packages, nil
return packages, commandLocations, nil
} }

108
internal/parser/parser.go

@ -20,21 +20,62 @@ package parser
import ( import (
"errors" "errors"
"fmt" "fmt"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// ParseResult is the standard representation for am image declaration // ParseResult is the standard representation for am image declaration
type ParseResult struct { type ParseResult struct {
Stages []*Stage
FilePath string
Stages []*Stage
}
// String implements fmt.Stringer interface
func (pr ParseResult) String() string {
return fmt.Sprintf(
"%s with %d stage(s)",
pr.FilePath,
len(pr.Stages),
)
} }
// Stage is the representation of a stage in the image declaration // Stage is the representation of a stage in the image declaration
type Stage struct { type Stage struct {
Name string
Image *Image
Repositories []*Repository
Packages []*Package
Index int
Name string
Image *Image
Repositories []*Repository
Packages []*Package
StageLocation ContentLocation
CommandLocations []*ContentLocation
}
// String implements fmt.Stringer interface
func (s Stage) String() string {
return fmt.Sprintf(
"%d %s %s",
s.Index,
s.Name,
s.Image,
)
}
// ContentLocation information about where a specific set of information exists within the file/manifest
type ContentLocation struct {
StartLine int
StartCharacter int
EndLine int
EndCharacter int
}
// String implements fmt.Stringer interface
func (cl ContentLocation) String() string {
return fmt.Sprintf(
"%d.%d:%d.%d",
cl.StartLine,
cl.StartCharacter,
cl.EndLine,
cl.EndCharacter,
)
} }
// Image is the representation of the reference to a container image layer // Image is the representation of the reference to a container image layer
@ -43,17 +84,43 @@ type Image struct {
Tag string Tag string
} }
// String implements fmt.Stringer interface
func (i Image) String() string {
return fmt.Sprintf(
"%s:%s",
i.Name,
i.Tag,
)
}
// Package is the representation of an installed pinned package defined in an image stage declaration // Package is the representation of an installed pinned package defined in an image stage declaration
type Package struct { type Package struct {
Name string Name string
Version string Version string
} }
// String implements fmt.Stringer interface
func (p Package) String() string {
return fmt.Sprintf(
"%s=%s",
p.Name,
p.Version,
)
}
// Repository is the representation of an added repository defined in a stage declaration // Repository is the representation of an added repository defined in a stage declaration
type Repository struct { type Repository struct {
Information string Information string
} }
// String implements fmt.Stringer interface
func (r Repository) String() string {
return fmt.Sprintf(
"%s",
r.Information,
)
}
// CommandParserType represents the recognized type of parser usable for a specific image // CommandParserType represents the recognized type of parser usable for a specific image
type CommandParserType int type CommandParserType int
@ -61,12 +128,13 @@ const (
CommandParserTypeDebian CommandParserType = iota CommandParserTypeDebian CommandParserType = iota
) )
// CommandParserTypeIds is a lookup map between CommandParserType and a representation string
// commandParserTypeIds is a lookup map between CommandParserType and a representation string
var commandParserTypeIds = map[CommandParserType][]string{ var commandParserTypeIds = map[CommandParserType][]string{
CommandParserTypeDebian: {"debian"}, CommandParserTypeDebian: {"debian"},
} }
func lookupCommandParserTypeName(parserType CommandParserType) string {
// findCommandParserTypeName lookup the command parser name by type enum
func findCommandParserTypeName(parserType CommandParserType) string {
if parserTypeName, ok := commandParserTypeIds[parserType]; ok { if parserTypeName, ok := commandParserTypeIds[parserType]; ok {
return parserTypeName[0] return parserTypeName[0]
} else { } else {
@ -78,8 +146,8 @@ var availableCommandParsers = map[CommandParserType]CommandParser{}
// CommandParser handles parsing commands from RUN arguments dependent on the base image type // CommandParser handles parsing commands from RUN arguments dependent on the base image type
type CommandParser interface { type CommandParser interface {
GetRepositories(runCommand string) ([]*Repository, error)
GetPinnedPackages(runCommand string) ([]*Package, error)
GetRepositories(logger *log.Entry, runCommand string) ([]*Repository, error)
GetPinnedPackages(logger *log.Entry, runCommand string) ([]*Package, error)
} }
// InitCommandParsers exposes functionality to configure the behavior of mapping images to a CommandParser // InitCommandParsers exposes functionality to configure the behavior of mapping images to a CommandParser
@ -88,21 +156,29 @@ func InitCommandParsers() error {
// Registering known parsers // Registering known parsers
availableCommandParsers[CommandParserTypeDebian] = newDebianCommandParser() availableCommandParsers[CommandParserTypeDebian] = newDebianCommandParser()
// TODO: Add dynamic loading from configuration
return nil return nil
} }
func GetCommandParser(image Image) (*CommandParser, error) { func GetCommandParser(image Image) (*CommandParser, error) {
parserType, determineParserErr := determineParserForImage(image)
if determineParserErr != nil {
log.Fatalf("Failed to determine a matching CommandParser for %s: %s", image, determineParserErr)
return nil, determineParserErr
parserType, err := determineParserForImage(image)
if err != nil {
log.WithFields(log.Fields{
"imageName": image.Name,
"imageTag": image.Tag,
"sourceError": err,
}).Fatal("failed to determine a matching CommandParser for image")
return nil, err
} }
if requestedCommandParser, ok := availableCommandParsers[parserType]; ok { if requestedCommandParser, ok := availableCommandParsers[parserType]; ok {
return &requestedCommandParser, nil return &requestedCommandParser, nil
} else { } else {
message := fmt.Sprintf("Unable to identify parser for %s", lookupCommandParserTypeName(parserType))
log.Fatal(message)
message := "unable to identify command parser type"
log.WithFields(log.Fields{
"commandParserType": findCommandParserTypeName(parserType),
}).Fatal(message)
return nil, errors.New(message) return nil, errors.New(message)
} }
} }

Loading…
Cancel
Save