Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions api/admin/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,38 @@ func UpdateSettings(c *gin.Context) {

l.Infof("platform admin: updating starlark exec limit to %d", *input.StarlarkExecLimit)
}

if input.BlockedImages != nil {
for _, restriction := range input.GetBlockedImages() {
if restriction.GetImage() == "" {
retErr := fmt.Errorf("blocked image entry missing image pattern")

util.HandleError(c, http.StatusBadRequest, retErr)

return
}
}

_s.SetBlockedImages(input.GetBlockedImages())

l.Infof("platform admin: updating blocked images to: %v", input.GetBlockedImages())
}

if input.WarnImages != nil {
for _, restriction := range input.GetWarnImages() {
if restriction.GetImage() == "" {
retErr := fmt.Errorf("warn image entry missing image pattern")

util.HandleError(c, http.StatusBadRequest, retErr)

return
}
}

_s.SetWarnImages(input.GetWarnImages())

l.Infof("platform admin: updating warn images to: %v", input.GetWarnImages())
}
}

if input.Queue != nil {
Expand Down
20 changes: 20 additions & 0 deletions api/build/compile_publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,26 @@ func CompileAndPublish(
"repo": r.GetName(),
"repo_id": r.GetID(),
}).Info("pipeline created")
} else {
// reset the pipeline warnings if the compiled pipeline returned any
//
// The pipeline warnings can change at any time due to the image registry since
// new images could be added to the blocked or warning list. To account for this,
// we update the pipeline warnings to match what was compiled with the latest
// results. In general, this shouldn't be called often since we create a new
// pipeline record for every new commit so this covers scenarios where this
// isn't the case such as restarting a build.
if len(compiled.GetWarnings()) > 0 {
pipeline.SetWarnings(compiled.GetWarnings())

// send API call to update the pipeline
pipeline, err = database.UpdatePipeline(ctx, pipeline)
if err != nil {
retErr := fmt.Errorf("%s: failed to update pipeline for %s: %w", baseErr, r.GetFullName(), err)

return nil, nil, http.StatusInternalServerError, retErr
}
}
}

b.SetPipelineID(pipeline.GetID())
Expand Down
128 changes: 124 additions & 4 deletions api/types/settings/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,128 @@ package settings

import "fmt"

// ImageRestriction represents a container image pattern that is either
// blocked or warned about when used in a pipeline.
type ImageRestriction struct {
Image *string `json:"image,omitempty" yaml:"image,omitempty"`
Reason *string `json:"reason,omitempty" yaml:"reason,omitempty"`
}

// GetImage returns the Image field.
//
// When the provided ImageRestriction type is nil, or the field within
// the type is nil, it returns the zero value for the field.
func (ir *ImageRestriction) GetImage() string {
if ir == nil || ir.Image == nil {
return ""
}

return *ir.Image
}

// GetReason returns the Reason field.
//
// When the provided ImageRestriction type is nil, or the field within
// the type is nil, it returns the zero value for the field.
func (ir *ImageRestriction) GetReason() string {
if ir == nil || ir.Reason == nil {
return ""
}

return *ir.Reason
}

// SetImage sets the Image field.
//
// When the provided ImageRestriction type is nil, it
// will set nothing and immediately return.
func (ir *ImageRestriction) SetImage(v string) {
if ir == nil {
return
}

ir.Image = &v
}

// SetReason sets the Reason field.
//
// When the provided ImageRestriction type is nil, it
// will set nothing and immediately return.
func (ir *ImageRestriction) SetReason(v string) {
if ir == nil {
return
}

ir.Reason = &v
}

// String implements the Stringer interface for the ImageRestriction type.
func (ir *ImageRestriction) String() string {
return fmt.Sprintf(`{
Image: %s,
Reason: %s,
}`,
ir.GetImage(),
ir.GetReason(),
)
}

type Compiler struct {
CloneImage *string `json:"clone_image,omitempty" yaml:"clone_image,omitempty"`
TemplateDepth *int `json:"template_depth,omitempty" yaml:"template_depth,omitempty"`
StarlarkExecLimit *int64 `json:"starlark_exec_limit,omitempty" yaml:"starlark_exec_limit,omitempty"`
CloneImage *string `json:"clone_image,omitempty" yaml:"clone_image,omitempty"`
TemplateDepth *int `json:"template_depth,omitempty" yaml:"template_depth,omitempty"`
StarlarkExecLimit *int64 `json:"starlark_exec_limit,omitempty" yaml:"starlark_exec_limit,omitempty"`
BlockedImages *[]ImageRestriction `json:"blocked_images,omitempty" yaml:"blocked_images,omitempty"`
WarnImages *[]ImageRestriction `json:"warn_images,omitempty" yaml:"warn_images,omitempty"`
}

// GetBlockedImages returns the BlockedImages field.
//
// When the provided Compiler type is nil, or the field within
// the type is nil, it returns the zero value for the field.
func (cs *Compiler) GetBlockedImages() []ImageRestriction {
if cs == nil || cs.BlockedImages == nil {
return []ImageRestriction{}
}

return *cs.BlockedImages
}

// GetCloneImage returns the CloneImage field.
// GetWarnImages returns the WarnImages field.
//
// When the provided Compiler type is nil, or the field within
// the type is nil, it returns the zero value for the field.
func (cs *Compiler) GetWarnImages() []ImageRestriction {
if cs == nil || cs.WarnImages == nil {
return []ImageRestriction{}
}

return *cs.WarnImages
}

// SetBlockedImages sets the BlockedImages field.
//
// When the provided Compiler type is nil, it
// will set nothing and immediately return.
func (cs *Compiler) SetBlockedImages(v []ImageRestriction) {
if cs == nil {
return
}

cs.BlockedImages = &v
}

// SetWarnImages sets the WarnImages field.
//
// When the provided Compiler type is nil, it
// will set nothing and immediately return.
func (cs *Compiler) SetWarnImages(v []ImageRestriction) {
if cs == nil {
return
}

cs.WarnImages = &v
}

// When the provided Compiler type is nil, or the field within
// the type is nil, it returns the zero value for the field.
func (cs *Compiler) GetCloneImage() string {
Expand Down Expand Up @@ -94,10 +208,14 @@ func (cs *Compiler) String() string {
CloneImage: %s,
TemplateDepth: %d,
StarlarkExecLimit: %d,
BlockedImages: %v,
WarnImages: %v,
}`,
cs.GetCloneImage(),
cs.GetTemplateDepth(),
cs.GetStarlarkExecLimit(),
cs.GetBlockedImages(),
cs.GetWarnImages(),
)
}

Expand All @@ -107,6 +225,8 @@ func CompilerMockEmpty() Compiler {
cs.SetCloneImage("")
cs.SetTemplateDepth(0)
cs.SetStarlarkExecLimit(0)
cs.SetBlockedImages(nil)
cs.SetWarnImages(nil)

return cs
}
70 changes: 70 additions & 0 deletions api/types/settings/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,46 @@ import (
"testing"
)

func TestTypes_ImageRestriction_String(t *testing.T) {
// setup types
blocked := ImageRestriction{
Image: new("docker.io/blocked/image:latest"),
Reason: new("this image is blocked"),
}

warning := ImageRestriction{
Image: new("docker.io/deprecated/image:latest"),
Reason: new("this image is deprecated"),
}

wantBlocked := fmt.Sprintf(`{
Image: %s,
Reason: %s,
}`,
blocked.GetImage(),
blocked.GetReason(),
)

wantWarning := fmt.Sprintf(`{
Image: %s,
Reason: %s,
}`,
warning.GetImage(),
warning.GetReason(),
)

// run test
gotBlocked := blocked.String()
if !reflect.DeepEqual(gotBlocked, wantBlocked) {
t.Errorf("String is %v, want %v", gotBlocked, wantBlocked)
}

gotWarning := warning.String()
if !reflect.DeepEqual(gotWarning, wantWarning) {
t.Errorf("String is %v, want %v", gotWarning, wantWarning)
}
}

func TestTypes_Compiler_Getters(t *testing.T) {
// setup tests
tests := []struct {
Expand Down Expand Up @@ -37,6 +77,14 @@ func TestTypes_Compiler_Getters(t *testing.T) {
if !reflect.DeepEqual(test.compiler.GetStarlarkExecLimit(), test.want.GetStarlarkExecLimit()) {
t.Errorf("GetStarlarkExecLimit is %v, want %v", test.compiler.GetStarlarkExecLimit(), test.want.GetStarlarkExecLimit())
}

if !reflect.DeepEqual(test.compiler.GetBlockedImages(), test.want.GetBlockedImages()) {
t.Errorf("GetBlockedImages is %v, want %v", test.compiler.GetBlockedImages(), test.want.GetBlockedImages())
}

if !reflect.DeepEqual(test.compiler.GetWarnImages(), test.want.GetWarnImages()) {
t.Errorf("GetWarnImages is %v, want %v", test.compiler.GetWarnImages(), test.want.GetWarnImages())
}
}
}

Expand Down Expand Up @@ -78,6 +126,18 @@ func TestTypes_Compiler_Setters(t *testing.T) {
if !reflect.DeepEqual(test.compiler.GetStarlarkExecLimit(), test.want.GetStarlarkExecLimit()) {
t.Errorf("SetStarlarkExecLimit is %v, want %v", test.compiler.GetStarlarkExecLimit(), test.want.GetStarlarkExecLimit())
}

test.compiler.SetBlockedImages(test.want.GetBlockedImages())

if !reflect.DeepEqual(test.compiler.GetBlockedImages(), test.want.GetBlockedImages()) {
t.Errorf("SetBlockedImages is %v, want %v", test.compiler.GetBlockedImages(), test.want.GetBlockedImages())
}

test.compiler.SetWarnImages(test.want.GetWarnImages())

if !reflect.DeepEqual(test.compiler.GetWarnImages(), test.want.GetWarnImages()) {
t.Errorf("SetWarnImages is %v, want %v", test.compiler.GetWarnImages(), test.want.GetWarnImages())
}
}
}

Expand All @@ -89,10 +149,14 @@ func TestTypes_Compiler_String(t *testing.T) {
CloneImage: %s,
TemplateDepth: %d,
StarlarkExecLimit: %d,
BlockedImages: %v,
WarnImages: %v,
}`,
cs.GetCloneImage(),
cs.GetTemplateDepth(),
cs.GetStarlarkExecLimit(),
cs.GetBlockedImages(),
cs.GetWarnImages(),
)

// run test
Expand All @@ -111,6 +175,12 @@ func testCompilerSettings() *Compiler {
cs.SetCloneImage("target/vela-git-slim:latest")
cs.SetTemplateDepth(1)
cs.SetStarlarkExecLimit(100)
cs.SetBlockedImages([]ImageRestriction{
{Image: new("docker.io/blocked/image:latest"), Reason: new("this image is blocked")},
})
cs.SetWarnImages([]ImageRestriction{
{Image: new("docker.io/deprecated/image:latest"), Reason: new("this image is deprecated")},
})

return cs
}
20 changes: 20 additions & 0 deletions compiler/native/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,16 @@ func (c *Client) compileSteps(ctx context.Context, p *yaml.Build, _pipeline *api
return nil, _pipeline, err
}

// check image restrictions (blocked → error, warned → warning)
imageWarnings, err := c.checkImageRestrictions(build)
if err != nil {
return nil, _pipeline, err
}

if len(imageWarnings) > 0 {
_pipeline.SetWarnings(append(_pipeline.GetWarnings(), imageWarnings...))
}

return build, _pipeline, nil
}

Expand Down Expand Up @@ -517,6 +527,16 @@ func (c *Client) compileStages(ctx context.Context, p *yaml.Build, _pipeline *ap
return nil, _pipeline, err
}

// check image restrictions (blocked → error, warned → warning)
imageWarnings, err := c.checkImageRestrictions(build)
if err != nil {
return nil, _pipeline, err
}

if len(imageWarnings) > 0 {
_pipeline.SetWarnings(append(_pipeline.GetWarnings(), imageWarnings...))
}

return build, _pipeline, nil
}

Expand Down
5 changes: 5 additions & 0 deletions compiler/native/native.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ func FromCLICommand(ctx context.Context, cmd *cli.Command) (*Client, error) {
c.UsePrivateGithub = true
}

c.SetBlockedImages(nil)
c.SetWarnImages(nil)

c.TemplateCache = make(map[string][]byte)

return c, nil
Expand Down Expand Up @@ -140,6 +143,8 @@ func (c *Client) Duplicate() compiler.Engine {
cc.CloneImage = c.CloneImage
cc.TemplateDepth = c.TemplateDepth
cc.StarlarkExecLimit = c.StarlarkExecLimit
cc.BlockedImages = c.BlockedImages
cc.WarnImages = c.WarnImages
cc.TemplateCache = make(map[string][]byte)

return cc
Expand Down
Loading
Loading