constellation/hack/oci-pin/internal/inject/inject.go

131 lines
3.7 KiB
Go
Raw Normal View History

/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
// inject renders Go source files with injected pinning values.
package inject
import (
"errors"
"io"
"regexp"
"text/template"
)
// Render renders the source code to inject the pinned values.
func Render(out io.Writer, vals PinningValues) error {
if err := validate(vals); err != nil {
return err
}
return sourceTpl.Execute(out, vals)
}
// validate validates the values to inject.
func validate(vals PinningValues) error {
if vals.Package == "" {
return errors.New("package is empty")
}
if vals.Ident == "" {
return errors.New("identifier is empty")
}
if vals.Registry == "" {
return errors.New("registry is empty")
}
if vals.Name == "" {
return errors.New("name is empty")
}
if vals.Digest == "" {
return errors.New("digest is empty")
}
if matched := packageNameRegexp.MatchString(vals.Package); !matched {
return errors.New("package is not valid")
}
if matched := identRegexp.MatchString(vals.Ident); !matched {
return errors.New("identifier is not valid")
}
if matched := registryRegexp.MatchString(vals.Registry); !matched {
return errors.New("registry is not valid")
}
if matched := prefixRegexp.MatchString(vals.Prefix); !matched {
return errors.New("prefix is not valid")
}
if matched := nameRegexp.MatchString(vals.Name); !matched {
return errors.New("name is not valid")
}
if matched := tagRegexp.MatchString(vals.Tag); vals.Tag != "" && !matched {
return errors.New("tag is not valid")
}
if matched := digestRegexp.MatchString(vals.Digest); !matched {
return errors.New("digest is not valid")
}
return nil
}
// PinningValues contains the values to inject into the generated source code.
type PinningValues struct {
// Package is the name of the package to generate.
Package string
// Ident is the base identifier of the generated constants.
Ident string
// Registry string
Registry string
// Prefix is the prefix of the container image name.
Prefix string
// Name is the name of the container image.
Name string
// Tag is the (optional) tag of the container image.
Tag string
// Digest is the digest of the container image.
Digest string
}
var (
sourceTpl = template.Must(template.New("goSource").Parse(goSourceTpl))
// packageNameRegexp is the regular expression for a valid package name.
packageNameRegexp = regexp.MustCompile(`^[a-z][a-z0-9_]*$`)
// identRegexp is the regular expression for a valid identifier.
identRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
// registryRegexp is the regular expression for a valid registry.
registryRegexp = regexp.MustCompile(`^[^\s/"]+$`)
// prefixRegexp is the regular expression for a valid prefix.
prefixRegexp = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9-_/]*)?$`)
// nameRegexp is the regular expression for a valid name.
nameRegexp = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9-_]*$`)
// tagRegexp is the regular expression for a valid tag.
tagRegexp = regexp.MustCompile(`[\w][\w.-]{0,127}`)
// digestRegexp is the regular expression for a valid digest.
digestRegexp = regexp.MustCompile(`^sha256:[a-f0-9]{64}$`)
)
const goSourceTpl = `package {{.Package}}
// Code generated by oci-pin. DO NOT EDIT.
const (
// {{.Ident}}Registry is the {{.Name}} container image registry.
{{.Ident}}Registry = "{{.Registry}}"
{{if .Prefix }} // {{.Ident}}Prefix is the {{.Name}} container image prefix.
{{.Ident}}Prefix = "{{.Prefix}}"
{{end}} // {{.Ident}}Name is the {{.Name}} container image short name part.
{{.Ident}}Name = "{{.Name}}"
{{if .Tag }} // {{.Ident}}Tag is the tag for the {{.Name}} container image.
{{.Ident}}Tag = "{{.Tag}}"
{{end}} // {{.Ident}}Digest is the digest for the {{.Name}} container image.
{{.Ident}}Digest = "{{.Digest}}"
)
`