Skip to content

Commit

Permalink
Merge branch 'main' into alokshin/CCIE-1940-Happy-RDev-stack-delete-a…
Browse files Browse the repository at this point in the history
…ction-should-also-delete-related-ECR-images
  • Loading branch information
alexlokshin-czi committed Oct 24, 2023
2 parents a5e5542 + f2065cd commit d9d8010
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 26 deletions.
51 changes: 26 additions & 25 deletions cli/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,49 +301,50 @@ func validateConfigurationIntegirty(ctx context.Context, slice string, happyClie
logrus.Debug("Scheduling validateConfigurationIntegirty()")
return func() error {
logrus.Debug("Running validateConfigurationIntegirty()")
// These services are configured in docker-compose.yml, and have their containers built
availableServices, err := happyClient.ArtifactBuilder.GetServices(ctx)

// Happy configuration is spread across these files:
// * .happy/config.json defines environments, specifies services, slices, features and tasks
// * docker-compose.yml defines services and their build configuration
// * terraform code from .happy/terraform/envs/<ENVNAME>/*.tf references services and their settings

// All services referenced through TF code must be present in config.json. All services listed in config.json
// must be declared in docker-compose.yml and have a build section.

// These services are configured in docker-compose.yml
composeServices, err := happyClient.ArtifactBuilder.GetAllServices(ctx)
if err != nil {
return errors.Wrap(err, "unable to get available services")
}

// NOTE: availableServices will only contain the services that are a part of the slice.
// That means we have to iterate over those first to make sure they are all in the config.json and
// not the other way around.
// ConfigServices are configured in config.json, and are a subset of the services in docker-compose.yml.
// Every service from config.json must be present in docker-compose.yml and must have a build section.
configServices := happyClient.HappyConfig.GetServices()
ss := sets.NewStringSet().Add(configServices...)
for serviceName, service := range availableServices {
// ignore services that don't have a build section
// as these are not deployable services
if service.Build == nil {
continue
}
ok := ss.ContainsElement(serviceName)
for _, serviceName := range configServices {
service, ok := composeServices[serviceName]
if !ok {
return errors.Errorf("service %s was referenced in docker-compose.yml, but not referenced in .happy/config.json services array", serviceName)
return errors.Errorf("service '%s' is not configured in docker-compose.yml, but referenced in your .happy/config.json services array", serviceName)
}
if service.Build == nil {
return errors.Errorf("service '%s' is not configured to be built in docker-compose.yml, but referenced in your .happy/config.json services array", serviceName)
}
}

// These services are referenced in terraform code for the environment
// These services are referenced in terraform code for the environment, and must be present in config.json
// (and docker-compose.yml as well -- see the check above).
srcDir := filepath.Join(happyClient.HappyConfig.GetProjectRoot(), happyClient.HappyConfig.TerraformDirectory())
deployedServices, err := tf.NewTfParser().ParseServices(srcDir)
if err != nil {
return errors.Wrap(err, "unable to parse terraform code")
}
for service := range deployedServices {
if _, ok := availableServices[service]; !ok {
return errors.Errorf("service %s is not configured in docker-compose.yml, but referenced in your terraform code", service)
}
found := false
for _, configuredService := range happyClient.HappyConfig.GetServices() {
if service == configuredService {
found = true
break
}
for serviceName := range deployedServices {
if _, ok := composeServices[serviceName]; !ok {
return errors.Errorf("service '%s' is not configured in docker-compose.yml, but referenced in your terraform code", serviceName)
}
found := ss.ContainsElement(serviceName)

if !found {
return errors.Errorf("service %s is not configured in ./happy/config.json, but referenced in your terraform code", service)
return errors.Errorf("service %s is not configured in ./happy/config.json, but referenced in your terraform code", serviceName)
}
}

Expand Down
2 changes: 1 addition & 1 deletion cli/pkg/artifact_builder/COVERAGE
Original file line number Diff line number Diff line change
@@ -1 +1 @@
55.8
55.8
11 changes: 11 additions & 0 deletions cli/pkg/artifact_builder/artifact_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,3 +641,14 @@ func parseRepositoryURL(url string) (registryId string, repositoryName string, e
}
return registryId, repositoryName, nil
}

func (ab ArtifactBuilder) GetAllServices(ctx context.Context) (map[string]ServiceConfig, error) {
bc := ab.config.Clone()
bc.configData = nil
bc.Profile = nil
config, err := bc.GetConfigData(ctx)
if err != nil {
return nil, errors.Wrap(err, "unable to get config data")
}
return config.Services, nil
}
1 change: 1 addition & 0 deletions cli/pkg/artifact_builder/artifact_builder_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type ArtifactBuilderIface interface {
BuildAndPush(ctx context.Context) error
DeleteImages(ctx context.Context, tag string) error
GetServices(ctx context.Context) (map[string]ServiceConfig, error)
GetAllServices(ctx context.Context) (map[string]ServiceConfig, error)
}

func CreateArtifactBuilder(ctx context.Context) ArtifactBuilderIface {
Expand Down
14 changes: 14 additions & 0 deletions cli/pkg/artifact_builder/builder_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ func NewBuilderConfig() *BuilderConfig {
}
}

func (b *BuilderConfig) Clone() *BuilderConfig {
return &BuilderConfig{
composeFile: b.composeFile,
composeEnvFile: b.composeEnvFile,
dockerRepo: b.dockerRepo,
env: b.env,
StackName: b.StackName,
Profile: b.Profile,
configData: b.configData,
Executor: b.Executor,
DryRun: b.DryRun,
}
}

func (b *BuilderConfig) WithBootstrap(bootstrap *config.Bootstrap) *BuilderConfig {
b.composeFile = bootstrap.DockerComposeConfigPath
return b
Expand Down
11 changes: 11 additions & 0 deletions cli/pkg/artifact_builder/dry_run_artifact_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,14 @@ func (ab *DryRunArtifactBuilder) GetServices(ctx context.Context) (map[string]Se
func (ab *DryRunArtifactBuilder) DeleteImages(ctx context.Context, tag string) error {
return nil
}

func (ab *DryRunArtifactBuilder) GetAllServices(ctx context.Context) (map[string]ServiceConfig, error) {
bc := ab.config.Clone()
bc.configData = nil
bc.Profile = nil
config, err := bc.GetConfigData(ctx)
if err != nil {
return nil, errors.Wrap(err, "unable to get config data")
}
return config.Services, nil
}

0 comments on commit d9d8010

Please sign in to comment.