package recipe import ( "fmt" "io/ioutil" "path/filepath" "strings" "coopcloud.tech/abra/pkg/formatter" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/stack" loader "coopcloud.tech/abra/pkg/upstream/stack" "github.com/distribution/reference" composetypes "github.com/docker/cli/cli/compose/types" ) // UpdateTag updates an image tag in-place on file system local compose files. func (r Recipe2) UpdateTag(image, tag string) (bool, error) { fullPattern := fmt.Sprintf("%s/compose**yml", r.Dir) image = formatter.StripTagMeta(image) composeFiles, err := filepath.Glob(fullPattern) if err != nil { return false, err } log.Debugf("considering %s config(s) for tag update", strings.Join(composeFiles, ", ")) for _, composeFile := range composeFiles { opts := stack.Deploy{Composefiles: []string{composeFile}} sampleEnv, err := r.SampleEnv() 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: log.Debugf("unable to parse %s, skipping", img) continue } composeImage := formatter.StripTagMeta(reference.Path(img)) log.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) log.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 (r Recipe2) UpdateLabel(pattern, serviceName, label string) error { fullPattern := fmt.Sprintf("%s/%s", r.Dir, pattern) composeFiles, err := filepath.Glob(fullPattern) if err != nil { return err } log.Debugf("considering %s config(s) for label update", strings.Join(composeFiles, ", ")) for _, composeFile := range composeFiles { opts := stack.Deploy{Composefiles: []string{composeFile}} sampleEnv, err := r.SampleEnv() 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") && strings.Contains(oldLabel, "version") { 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 { log.Warnf("%s is already set, nothing to do?", label) return nil } log.Debugf("updating %s to %s in %s", old, label, compose.Filename) if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0764); err != nil { return err } log.Infof("synced label %s to service %s", label, serviceName) } } if !discovered { log.Warn("no existing label found, automagic insertion not supported yet") log.Fatalf("add '- \"%s\"' manually to the 'app' service in %s", label, composeFile) } } return nil }