func (r *RedisBackend) ListRegistrations(env string) ([]ServiceRegistration, error) { keys, err := r.Keys(path.Join(env, "*", "hosts", "*", "*", "*")) if err != nil { return nil, err } var regList []ServiceRegistration for _, key := range keys { val, err := r.Get(key, "location") if err != nil { log.Warnf("WARN: Unable to get location for %s: %s", key, err) continue } svcReg := ServiceRegistration{ Name: path.Base(key), } err = json.Unmarshal([]byte(val), &svcReg) if err != nil { log.Warnf("WARN: Unable to unmarshal JSON for %s: %s", key, err) continue } svcReg.Path = key regList = append(regList, svcReg) } return regList, nil }
func (s *ServiceRuntime) instanceIds(app, versionId string) ([]int, error) { containers, err := s.ManagedContainers() if err != nil { return []int{}, err } instances := []int{} for _, c := range containers { ga := s.EnvFor(c)["GALAXY_APP"] if ga != app { continue } gi := s.EnvFor(c)["GALAXY_INSTANCE"] gv := s.EnvFor(c)["GALAXY_VERSION"] if gi != "" { i, err := strconv.ParseInt(gi, 10, 64) if err != nil { log.Warnf("WARN: Invalid number %s for %s. Ignoring.", gi, c.ID[:12]) continue } if versionId != "" && gv != versionId { continue } instances = append(instances, int(i)) } } return instances, nil }
func AppAssign(configStore *config.Store, app, env, pool string) error { // Don't allow deleting runtime hosts entries if app == "hosts" || app == "pools" { return fmt.Errorf("invalid app name: %s", app) } exists, err := configStore.PoolExists(env, pool) if err != nil { return err } if !exists { log.Warnf("WARN: Pool %s does not exist.", pool) } created, err := configStore.AssignApp(app, env, pool) if err != nil { return err } if created { log.Printf("Assigned %s in env %s to pool %s.\n", app, env, pool) } else { log.Printf("%s already assigned to pool %s in env %s.\n", app, pool, env) } return nil }
func ConfigUnset(configStore *config.Store, app, env string, envVars []string) error { if len(envVars) == 0 { return fmt.Errorf("no config values specified.") } svcCfg, err := configStore.GetApp(app, env) if err != nil { return fmt.Errorf("unable to unset config: %s.", err) } updated := false for _, arg := range envVars { k := strings.ToUpper(strings.TrimSpace(arg)) if k == "ENV" || svcCfg.EnvGet(k) == "" { log.Warnf("%s cannot be unset.", k) continue } log.Printf("%s\n", k) svcCfg.EnvSet(strings.ToUpper(arg), "") updated = true } if !updated { return fmt.Errorf("Configuration NOT changed for %s", app) } updated, err = configStore.UpdateApp(svcCfg, env) if err != nil { return fmt.Errorf("ERROR: Unable to unset config: %s.", err) } if !updated { return fmt.Errorf("Configuration NOT changed for %s", app) } log.Printf("Configuration changed for %s. v%d.\n", app, svcCfg.ID()) return nil }
// Stop any running galaxy containers that are not assigned to us // TODO: We call ManagedContainers a lot, repeatedly listing and inspecting all containers. func (s *ServiceRuntime) StopUnassigned(env, pool string) error { containers, err := s.ManagedContainers() if err != nil { return err } for _, container := range containers { name := s.EnvFor(container)["GALAXY_APP"] pools, err := s.configStore.ListAssignedPools(env, name) if err != nil { log.Errorf("ERROR: Unable to list pool assignments for %s: %s", container.Name, err) continue } if len(pools) == 0 || !utils.StringInSlice(pool, pools) { log.Warnf("galaxy container %s not assigned to %s/%s", container.Name, env, pool) s.stopContainer(container) } } return nil }
func (c *ConsulBackend) ListRegistrations(env string) ([]ServiceRegistration, error) { prefix := path.Join("galaxy", "services", env) kvPairs, _, err := c.client.KV().List(prefix, nil) if err != nil { return nil, err } regList := []ServiceRegistration{} for _, kvp := range kvPairs { svcReg := ServiceRegistration{ Name: path.Base(kvp.Key), } err = json.Unmarshal(kvp.Value, &svcReg) if err != nil { log.Warnf("WARN: Unable to unmarshal JSON for %s: %s", kvp.Key, err) continue } regList = append(regList, svcReg) } return regList, nil }
func (s *ServiceRuntime) Start(env, pool string, appCfg config.App) (*docker.Container, error) { img := appCfg.Version() imgIdRef := appCfg.Version() if appCfg.VersionID() != "" { imgIdRef = appCfg.VersionID() } // see if we have the image locally image, err := s.PullImage(img, imgIdRef) if err != nil { return nil, err } // setup env vars from etcd var envVars []string envVars = append(envVars, "ENV"+"="+env) for key, value := range appCfg.Env() { if key == "ENV" { continue } envVars = append(envVars, strings.ToUpper(key)+"="+s.replaceVarEnv(value, s.hostIP)) } instanceId, err := s.NextInstanceSlot(appCfg.Name(), strconv.FormatInt(appCfg.ID(), 10)) if err != nil { return nil, err } envVars = append(envVars, fmt.Sprintf("HOST_IP=%s", s.hostIP)) envVars = append(envVars, fmt.Sprintf("GALAXY_APP=%s", appCfg.Name())) envVars = append(envVars, fmt.Sprintf("GALAXY_VERSION=%s", strconv.FormatInt(appCfg.ID(), 10))) envVars = append(envVars, fmt.Sprintf("GALAXY_INSTANCE=%s", strconv.FormatInt(int64(instanceId), 10))) publicDns, err := EC2PublicHostname() if err != nil { log.Warnf("Unable to determine public hostname. Not on AWS? %s", err) publicDns = "127.0.0.1" } envVars = append(envVars, fmt.Sprintf("PUBLIC_HOSTNAME=%s", publicDns)) containerName := appCfg.ContainerName() + "." + strconv.FormatInt(int64(instanceId), 10) container, err := s.dockerClient.InspectContainer(containerName) _, ok := err.(*docker.NoSuchContainer) if err != nil && !ok { return nil, err } // Existing container is running or stopped. If the image has changed, stop // and re-create it. if container != nil && container.Image != image.ID { if container.State.Running || container.State.Restarting || container.State.Paused { log.Printf("Stopping %s version %s running as %s", appCfg.Name(), appCfg.Version(), container.ID[0:12]) err := s.dockerClient.StopContainer(container.ID, 10) if err != nil { return nil, err } } log.Printf("Removing %s version %s running as %s", appCfg.Name(), appCfg.Version(), container.ID[0:12]) err = s.dockerClient.RemoveContainer(docker.RemoveContainerOptions{ ID: container.ID, }) if err != nil { return nil, err } container = nil } if container == nil { config := &docker.Config{ Image: img, Env: envVars, } mem := appCfg.GetMemory(pool) if mem != "" { m, err := utils.ParseMemory(mem) if err != nil { return nil, err } config.Memory = m } cpu := appCfg.GetCPUShares(pool) if cpu != "" { if c, err := strconv.Atoi(cpu); err == nil { config.CPUShares = int64(c) } } log.Printf("Creating %s version %s", appCfg.Name(), appCfg.Version()) container, err = s.dockerClient.CreateContainer(docker.CreateContainerOptions{ Name: containerName, Config: config, }) if err != nil { return nil, err } } log.Printf("Starting %s version %s running as %s", appCfg.Name(), appCfg.Version(), container.ID[0:12]) config := &docker.HostConfig{ PublishAllPorts: true, RestartPolicy: docker.RestartPolicy{ Name: "on-failure", MaximumRetryCount: 16, }, } if s.dns != "" { config.DNS = []string{s.dns} } err = s.dockerClient.StartContainer(container.ID, config) return container, err }
func (s *ServiceRuntime) StartInteractive(env, pool string, appCfg config.App) error { // see if we have the image locally fmt.Fprintf(os.Stderr, "Pulling latest image for %s\n", appCfg.Version()) _, err := s.PullImage(appCfg.Version(), appCfg.VersionID()) if err != nil { return err } args := []string{ "run", "--rm", "-i", } args = append(args, "-e") args = append(args, "ENV"+"="+env) for key, value := range appCfg.Env() { if key == "ENV" { continue } args = append(args, "-e") args = append(args, strings.ToUpper(key)+"="+s.replaceVarEnv(value, s.hostIP)) } args = append(args, "-e") args = append(args, fmt.Sprintf("HOST_IP=%s", s.hostIP)) if s.dns != "" { args = append(args, "--dns") args = append(args, s.dns) } args = append(args, "-e") args = append(args, fmt.Sprintf("GALAXY_APP=%s", appCfg.Name())) args = append(args, "-e") args = append(args, fmt.Sprintf("GALAXY_VERSION=%s", strconv.FormatInt(appCfg.ID(), 10))) instanceId, err := s.NextInstanceSlot(appCfg.Name(), strconv.FormatInt(appCfg.ID(), 10)) if err != nil { return err } args = append(args, "-e") args = append(args, fmt.Sprintf("GALAXY_INSTANCE=%s", strconv.FormatInt(int64(instanceId), 10))) publicDns, err := EC2PublicHostname() if err != nil { log.Warnf("Unable to determine public hostname. Not on AWS? %s", err) publicDns = "127.0.0.1" } args = append(args, "-e") args = append(args, fmt.Sprintf("PUBLIC_HOSTNAME=%s", publicDns)) mem := appCfg.GetMemory(pool) if mem != "" { args = append(args, "-m") args = append(args, mem) } cpu := appCfg.GetCPUShares(pool) if cpu != "" { args = append(args, "-c") args = append(args, cpu) } args = append(args, []string{"-t", appCfg.Version(), "/bin/bash"}...) // shell out to docker run to get signal forwarded and terminal setup correctly //cmd := exec.Command("docker", "run", "-rm", "-i", "-t", appCfg.Version(), "/bin/bash") cmd := exec.Command("docker", args...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Start() if err != nil { log.Fatal(err) } err = cmd.Wait() if err != nil { fmt.Fprintf(os.Stderr, "Command finished with error: %v\n", err) } return err }
func ConfigSet(configStore *config.Store, app, env string, envVars []string) error { if len(envVars) == 0 { bytes, err := ioutil.ReadAll(os.Stdin) if err != nil { return err } envVars = strings.Split(string(bytes), "\n") } if len(envVars) == 0 { return fmt.Errorf("no config values specified.") } svcCfg, err := configStore.GetApp(app, env) if err != nil { return fmt.Errorf("unable to set config: %s.", err) } if svcCfg == nil { svcCfg = configStore.NewAppConfig(app, "") } updated := false for _, arg := range envVars { if strings.TrimSpace(arg) == "" { continue } if !strings.Contains(arg, "=") { return fmt.Errorf("bad config variable format: %s", arg) } sep := strings.Index(arg, "=") k := strings.ToUpper(strings.TrimSpace(arg[0:sep])) v := strings.TrimSpace(arg[sep+1:]) if k == "ENV" { log.Warnf("%s cannot be updated.", k) continue } log.Printf("%s=%s\n", k, v) svcCfg.EnvSet(k, v) updated = true } if !updated { return fmt.Errorf("configuration NOT changed for %s", app) } updated, err = configStore.UpdateApp(svcCfg, env) if err != nil { return fmt.Errorf("unable to set config: %s.", err) } if !updated { return fmt.Errorf("configuration NOT changed for %s", app) } log.Printf("Configuration changed for %s. v%d\n", app, svcCfg.ID()) return nil }
// restore an app's config from backup func appRestore(c *cli.Context) { initStore(c) var err error var rawBackup []byte fileName := c.String("file") if fileName != "" { rawBackup, err = ioutil.ReadFile(fileName) if err != nil { log.Fatal(err) } } else { log.Println("Reading backup from STDIN") rawBackup, err = ioutil.ReadAll(os.Stdin) if err != nil { log.Fatal(err) } } backup := &backupData{} if err := json.Unmarshal(rawBackup, backup); err != nil { log.Fatal(err) } fmt.Println("Found backup from ", backup.Time) var toRestore []*appCfg if apps := c.Args(); len(apps) > 0 { for _, app := range apps { found := false for _, bkup := range backup.Apps { if bkup.Name == app { toRestore = append(toRestore, bkup) found = true break } } if !found { log.Fatalf("no backup found for '%s'\n", app) } } } else { toRestore = backup.Apps } // check for conflicts // NOTE: there is still a race here if an app is created after this check if !c.Bool("force") { needForce := false for _, bkup := range toRestore { exists, err := configStore.AppExists(bkup.Name, utils.GalaxyEnv(c)) if err != nil { log.Fatal(err) } if exists { log.Warnf("Cannot restore over existing app '%s'", bkup.Name) needForce = true } } if needForce { log.Fatal("Use -force to overwrite") } } loggedErr := false for _, bkup := range toRestore { if err := restoreApp(bkup, utils.GalaxyEnv(c)); err != nil { log.Errorf("%s", err) loggedErr = true } } if loggedErr { // This is mostly to give a non-zero exit status log.Fatal("Error occured during restore") } }