diff --git a/internal/layerfile/layerfile.go b/internal/layerfile/layerfile.go index 4fbbc82..d5c22b5 100644 --- a/internal/layerfile/layerfile.go +++ b/internal/layerfile/layerfile.go @@ -12,7 +12,10 @@ import ( "github.com/ergomake/layerform/pkg/data" ) -var ErrInvalidDefinitionName = errors.New("invalid layer definition name") +var ( + ErrInvalidDefinitionName = errors.New("invalid layer definition name") + ErrDependencyDoesNotExist = errors.New("dependency does not exist") +) var alphanumericRegex = regexp.MustCompile("^[A-Za-z0-9][A-Za-z0-9_-]*[A-Za-z0-9]$") @@ -40,6 +43,10 @@ func FromFile(sourceFilepath string) (*layerfile, error) { } func (lf *layerfile) ToLayers() ([]*data.LayerDefinition, error) { + if err := lf.validateLayersDependencies(); err != nil { + return nil, errors.Wrap(err, "fail to validate layers dependencies") + } + dir := path.Dir(lf.sourceFilepath) dataLayers := make([]*data.LayerDefinition, len(lf.Layers)) @@ -89,3 +96,21 @@ func (lf *layerfile) ToLayers() ([]*data.LayerDefinition, error) { return dataLayers, nil } + +func (lf *layerfile) validateLayersDependencies() error { + names := make(map[string]struct{}) + + for _, l := range lf.Layers { + names[l.Name] = struct{}{} + } + + for _, l := range lf.Layers { + for _, d := range l.Dependencies { + if _, ok := names[d]; !ok { + return errors.Wrap(ErrDependencyDoesNotExist, d) + } + } + } + + return nil +} diff --git a/internal/layerfile/layerfile_test.go b/internal/layerfile/layerfile_test.go index 0ddc43c..593ea55 100644 --- a/internal/layerfile/layerfile_test.go +++ b/internal/layerfile/layerfile_test.go @@ -135,3 +135,71 @@ func TestToLayers_ValidateNameOfLayerDefinitions(t *testing.T) { }) } } + +func TestToLayers_ValidateAllDependenciesExist(t *testing.T) { + tests := []struct { + name string + lf layerfile + err error + }{ + { + name: "Dependencies don't exist", + lf: layerfile{ + Layers: []layerfileLayer{ + { + Name: "foo", + Dependencies: []string{"bar", "baz"}, + }, + { + Name: "bar", + }, + }, + }, + err: ErrDependencyDoesNotExist, + }, + { + name: "Dependencies exist", + lf: layerfile{ + Layers: []layerfileLayer{ + { + Name: "foo", + Dependencies: []string{"bar"}, + }, + { + Name: "bar", + }, + }, + }, + }, + { + name: "Layers have no dependencies", + lf: layerfile{ + Layers: []layerfileLayer{ + { + Name: "foo", + }, + { + Name: "bar", + }, + }, + }, + }, + { + name: "No layers", + lf: layerfile{ + Layers: []layerfileLayer{}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := tt.lf.ToLayers() + if tt.err == nil { + assert.NoError(t, err) + } else { + assert.ErrorIs(t, err, tt.err) + } + }) + } +}