package compose import ( "fmt" "io/ioutil" "path" "path/filepath" "strings" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/formatter" "coopcloud.tech/abra/pkg/upstream/stack" loader "coopcloud.tech/abra/pkg/upstream/stack" composetypes "github.com/docker/cli/cli/compose/types" "github.com/docker/distribution/reference" "github.com/sirupsen/logrus" ) // UpdateTag updates an image tag in-place on file system local compose files. func UpdateTag(pattern, image, tag, recipeName string) (bool, error) { composeFiles, err := filepath.Glob(pattern) if err != nil { return false, err } logrus.Debugf("considering %s config(s) for tag update", strings.Join(composeFiles, ", ")) for _, composeFile := range composeFiles { opts := stack.Deploy{Composefiles: []string{composeFile}} envSamplePath := path.Join(config.RECIPES_DIR, recipeName, ".env.sample") sampleEnv, err := config.ReadEnv(envSamplePath) if err != nil { return false, err } compose, err := loader.LoadComposefile(opts, sampleEnv) if err != nil { return false, err } for _, service := range compose.Services { if service.Image == "" { continue // may be a compose.$optional.yml file } img, _ := reference.ParseNormalizedNamed(service.Image) if err != nil { return false, err } var composeTag string switch img.(type) { case reference.NamedTagged: composeTag = img.(reference.NamedTagged).Tag() default: // unable to parse, typically image missing tag return false, nil } composeImage := formatter.StripTagMeta(reference.Path(img)) logrus.Debugf("parsed %s from %s", composeTag, service.Image) if image == composeImage { bytes, err := ioutil.ReadFile(composeFile) if err != nil { return false, err } old := fmt.Sprintf("%s:%s", composeImage, composeTag) new := fmt.Sprintf("%s:%s", composeImage, tag) replacedBytes := strings.Replace(string(bytes), old, new, -1) logrus.Debugf("updating %s to %s in %s", old, new, compose.Filename) if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0764); err != nil { return false, err } } } } return false, nil } // UpdateLabel updates a label in-place on file system local compose files. func UpdateLabel(pattern, serviceName, label, recipeName string) error { composeFiles, err := filepath.Glob(pattern) if err != nil { return err } logrus.Debugf("considering %s config(s) for label update", strings.Join(composeFiles, ", ")) for _, composeFile := range composeFiles { opts := stack.Deploy{Composefiles: []string{composeFile}} envSamplePath := path.Join(config.RECIPES_DIR, recipeName, ".env.sample") sampleEnv, err := config.ReadEnv(envSamplePath) if err != nil { return err } compose, err := loader.LoadComposefile(opts, sampleEnv) if err != nil { return err } serviceExists := false var service composetypes.ServiceConfig for _, s := range compose.Services { if s.Name == serviceName { service = s serviceExists = true } } if !serviceExists { continue } discovered := false for oldLabel, value := range service.Deploy.Labels { if strings.HasPrefix(oldLabel, "coop-cloud") { discovered = true bytes, err := ioutil.ReadFile(composeFile) if err != nil { return err } old := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", value) replacedBytes := strings.Replace(string(bytes), old, label, -1) if old == label { logrus.Warnf("%s is already set, nothing to do?", label) return nil } logrus.Debugf("updating %s to %s in %s", old, label, compose.Filename) if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0764); err != nil { return err } logrus.Infof("synced label %s to service %s", label, serviceName) } } if !discovered { logrus.Warn("no existing label found, automagic insertion not supported yet") logrus.Fatalf("add '- \"%s\"' manually to the 'app' service in %s", label, composeFile) } } return nil }