package compose import ( "fmt" "io/ioutil" "path" "path/filepath" "strings" "coopcloud.tech/abra/pkg/config" "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) error { composeFiles, err := filepath.Glob(pattern) if err != nil { return 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.ABRA_DIR, "apps", recipeName, ".env.sample") sampleEnv, err := config.ReadEnv(envSamplePath) if err != nil { return err } compose, err := loader.LoadComposefile(opts, sampleEnv) if err != nil { return 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 err } composeImage := reference.Path(img) if strings.Contains(composeImage, "library") { // ParseNormalizedNamed prepends 'library' to images like nginx:, // postgres:, i.e. images which do not have a username in the // first position of the string composeImage = strings.Split(composeImage, "/")[1] } composeTag := img.(reference.NamedTagged).Tag() logrus.Debugf("parsed '%s' from '%s'", composeTag, service.Image) if image == composeImage { bytes, err := ioutil.ReadFile(composeFile) if err != nil { return 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 err } } } } return 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.ABRA_DIR, "apps", 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, cannot continue...") logrus.Fatalf("add \"%s\" manually, automagic insertion not supported yet", label) } } return nil }