Beispiel #1
0
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
}
Beispiel #2
0
func pullImage(appCfg config.App) (*docker.Image, error) {

	image, err := serviceRuntime.InspectImage(appCfg.Version())
	if image != nil && image.ID == appCfg.VersionID() || appCfg.VersionID() == "" {
		return image, nil
	}

	log.Printf("Pulling %s version %s\n", appCfg.Name(), appCfg.Version())
	image, err = serviceRuntime.PullImage(appCfg.Version(),
		appCfg.VersionID())
	if image == nil || err != nil {
		log.Errorf("ERROR: Could not pull image %s: %s",
			appCfg.Version(), err)
		return nil, err
	}

	if image.ID != appCfg.VersionID() && len(appCfg.VersionID()) > 12 {
		log.Errorf("ERROR: Pulled image for %s does not match expected ID. Expected: %s: Got: %s",
			appCfg.Version(),
			image.ID[0:12], appCfg.VersionID()[0:12])
		return nil, errors.New(fmt.Sprintf("failed to pull image ID %s", appCfg.VersionID()[0:12]))
	}

	log.Printf("Pulled %s\n", appCfg.Version())
	return image, nil
}
Beispiel #3
0
func (s *ServiceRuntime) stopContainer(container *docker.Container) error {
	if _, ok := blacklistedContainerId[container.ID]; ok {
		log.Printf("Container %s blacklisted. Won't try to stop.\n", container.ID)
		return nil
	}

	log.Printf("Stopping %s container %s\n", strings.TrimPrefix(container.Name, "/"), container.ID[0:12])

	c := make(chan error, 1)
	go func() { c <- s.dockerClient.StopContainer(container.ID, 10) }()
	select {
	case err := <-c:
		if err != nil {
			log.Printf("ERROR: Unable to stop container: %s\n", container.ID)
			return err
		}
	case <-time.After(20 * time.Second):
		blacklistedContainerId[container.ID] = true
		log.Printf("ERROR: Timed out trying to stop container. Zombie?. Blacklisting: %s\n", container.ID)
		return nil
	}
	log.Printf("Stopped %s container %s\n", strings.TrimPrefix(container.Name, "/"), container.ID[0:12])

	return nil
	// TODO: why is this commented out?
	//       Should we verify that containers are actually removed somehow?
	/*	return s.dockerClient.RemoveContainer(docker.RemoveContainerOptions{
		ID:            container.ID,
		RemoveVolumes: true,
	})*/
}
Beispiel #4
0
func poolCreate(c *cli.Context) {
	ensureEnvArg(c)
	ensurePoolArg(c)
	initStore(c)
	created, err := configStore.CreatePool(utils.GalaxyPool(c), utils.GalaxyEnv(c))
	if err != nil {
		log.Fatalf("ERROR: Could not create pool: %s", err)
		return
	}

	if created {
		log.Printf("Pool %s created\n", utils.GalaxyPool(c))
	} else {
		log.Printf("Pool %s already exists\n", utils.GalaxyPool(c))
	}

	ec2host, err := runtime.EC2PublicHostname()
	if err != nil || ec2host == "" {
		log.Debug("not running from AWS, skipping pool creation")
		return
	}

	// now create the cloudformation stack
	// is this fails, the stack can be created separately with
	// stack:create_pool
	stackCreatePool(c)
}
Beispiel #5
0
func AppDeploy(configStore *config.Store, serviceRuntime *runtime.ServiceRuntime, app, env, version string) error {
	log.Printf("Pulling image %s...", version)

	image, err := serviceRuntime.PullImage(version, "")
	if image == nil || err != nil {
		return fmt.Errorf("unable to pull %s. Has it been released yet?", version)
	}

	svcCfg, err := configStore.GetApp(app, env)
	if err != nil {
		return fmt.Errorf("unable to deploy app: %s.", err)
	}

	if svcCfg == nil {
		return fmt.Errorf("app %s does not exist. Create it first.", app)
	}

	svcCfg.SetVersion(version)
	svcCfg.SetVersionID(utils.StripSHA(image.ID))

	updated, err := configStore.UpdateApp(svcCfg, env)
	if err != nil {
		return fmt.Errorf("could not store version: %s", err)
	}
	if !updated {
		return fmt.Errorf("%s NOT deployed.", version)
	}
	log.Printf("Deployed %s.\n", version)
	return nil
}
Beispiel #6
0
func ConfigList(configStore *config.Store, app, env string) error {

	cfg, err := configStore.GetApp(app, env)
	if err != nil {
		return err
	}

	if cfg == nil {
		return fmt.Errorf("unable to list config for %s.", app)
	}

	keys := sort.StringSlice{"ENV"}
	for k, _ := range cfg.Env() {
		keys = append(keys, k)
	}

	keys.Sort()

	for _, k := range keys {
		if k == "ENV" {
			log.Printf("%s=%s\n", k, env)
			continue
		}
		log.Printf("%s=%s\n", k, cfg.Env()[k])
	}

	return nil
}
Beispiel #7
0
func pgPsql(c *cli.Context) {
	ensureEnvArg(c)
	initStore(c)
	app := ensureAppParam(c, "pg:psql")

	appCfg, err := configStore.GetApp(app, utils.GalaxyEnv(c))
	if err != nil {
		log.Fatalf("ERROR: Unable to run command: %s.", err)
		return
	}

	database_url := appCfg.Env()["DATABASE_URL"]
	if database_url == "" {
		log.Printf("No DATABASE_URL configured.  Set one with config:set first.")
		return
	}

	if !strings.HasPrefix(database_url, "postgres://") {
		log.Printf("DATABASE_URL is not a postgres database.")
		return
	}

	cmd := exec.Command("psql", database_url)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// Ignore SIGINT while the process is running
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)

	defer func() {
		signal.Stop(ch)
		close(ch)
	}()

	go func() {
		for {
			_, ok := <-ch
			if !ok {
				break
			}
		}
	}()

	err = cmd.Start()
	if err != nil {
		log.Fatal(err)
	}

	err = cmd.Wait()
	if err != nil {
		fmt.Printf("Command finished with error: %v\n", err)
	}
}
Beispiel #8
0
func monitorService(changedConfigs chan *config.ConfigChange) {

	for {

		var changedConfig *config.ConfigChange
		select {

		case changedConfig = <-changedConfigs:

			if changedConfig.Error != nil {
				log.Errorf("ERROR: Error watching changes: %s", changedConfig.Error)
				continue
			}

			if changedConfig.AppConfig == nil {
				continue
			}

			assigned, err := appAssigned(changedConfig.AppConfig.Name())
			if err != nil {
				log.Errorf("ERROR: Error retrieving service config for %s: %s", changedConfig.AppConfig.Name(), err)
				if !loop {
					return
				}
				continue
			}

			if !assigned {
				continue
			}

			ch, ok := workerChans[changedConfig.AppConfig.Name()]
			if !ok {
				name := changedConfig.AppConfig.Name()
				ch := make(chan string)
				workerChans[name] = ch
				wg.Add(1)
				go restartContainers(name, ch)
				ch <- "deploy"

				log.Printf("Started new worker for %s\n", name)
				continue
			}

			if changedConfig.Restart {
				log.Printf("Restarting %s", changedConfig.AppConfig.Name())
				ch <- "restart"
			} else {
				ch <- "deploy"
			}
		}
	}

}
Beispiel #9
0
func (r *RedisBackend) subscribeChannel(key string, msgs chan string) {
	var wg sync.WaitGroup

	redisPool := redis.Pool{
		MaxIdle:     1,
		IdleTimeout: 0,
		Dial: func() (redis.Conn, error) {
			return redis.DialTimeout("tcp", r.RedisHost, time.Second, 0, 0)
		},
		// test every connection for now
		TestOnBorrow: r.testOnBorrow,
	}

	for {
		conn := redisPool.Get()
		// no defer, doesn't return
		if err := conn.Err(); err != nil {
			conn.Close()
			log.Printf("ERROR: %v\n", err)
			time.Sleep(5 * time.Second)
			continue
		}

		wg.Add(1)
		psc := redis.PubSubConn{Conn: conn}
		go func() {
			defer wg.Done()
			for {
				switch n := psc.Receive().(type) {
				case redis.Message:
					msg := string(n.Data)
					msgs <- msg
				case error:
					psc.Close()
					log.Printf("ERROR: %v\n", n)
					return
				}
			}
		}()

		wg.Add(1)
		go func() {
			defer wg.Done()
			psc.Subscribe(key)
			log.Printf("Monitoring for config changes on channel: %s\n", key)
		}()
		wg.Wait()
		conn.Close()
	}
}
Beispiel #10
0
func Register(serviceRuntime *runtime.ServiceRuntime, configStore *config.Store, env, pool, hostIP, shuttleAddr string) {
	if shuttleAddr != "" {
		client = shuttle.NewClient(shuttleAddr)
	}

	RegisterAll(serviceRuntime, configStore, env, pool, hostIP, shuttleAddr, false)

	containerEvents := make(chan runtime.ContainerEvent)
	err := serviceRuntime.RegisterEvents(env, pool, hostIP, containerEvents)
	if err != nil {
		log.Printf("ERROR: Unable to register docker event listener: %s", err)
	}

	for {

		select {
		case ce := <-containerEvents:
			switch ce.Status {
			case "start":
				reg, err := configStore.RegisterService(env, pool, hostIP, ce.Container)
				if err != nil {
					log.Errorf("ERROR: Unable to register container: %s", err)
					continue
				}

				log.Printf("Registered %s running as %s for %s%s", strings.TrimPrefix(reg.ContainerName, "/"),
					reg.ContainerID[0:12], reg.Name, locationAt(reg))
				registerShuttle(configStore, env, shuttleAddr)
			case "die", "stop":
				reg, err := configStore.UnRegisterService(env, pool, hostIP, ce.Container)
				if err != nil {
					log.Errorf("ERROR: Unable to unregister container: %s", err)
					continue
				}

				if reg != nil {
					log.Printf("Unregistered %s running as %s for %s%s", strings.TrimPrefix(reg.ContainerName, "/"),
						reg.ContainerID[0:12], reg.Name, locationAt(reg))
				}
				RegisterAll(serviceRuntime, configStore, env, pool, hostIP, shuttleAddr, true)
				pruneShuttleBackends(configStore, env, shuttleAddr)
			}

		case <-time.After(10 * time.Second):
			RegisterAll(serviceRuntime, configStore, env, pool, hostIP, shuttleAddr, true)
			pruneShuttleBackends(configStore, env, shuttleAddr)
		}
	}
}
Beispiel #11
0
func poolCreate(c *cli.Context) {
	ensureEnvArg(c)
	ensurePoolArg(c)
	initStore(c)
	created, err := configStore.CreatePool(utils.GalaxyPool(c), utils.GalaxyEnv(c))
	if err != nil {
		log.Fatalf("ERROR: Could not create pool: %s", err)
		return
	}

	if created {
		log.Printf("Pool %s created\n", utils.GalaxyPool(c))
	} else {
		log.Printf("Pool %s already exists\n", utils.GalaxyPool(c))
	}
}
Beispiel #12
0
func (s *ServiceRuntime) RegisterAll(env, pool, hostIP string) ([]*config.ServiceRegistration, error) {
	// make sure any old containers that shouldn't be running are gone
	// FIXME: I don't like how a "Register" function has the possible side
	//        effect of stopping containers
	s.StopUnassigned(env, pool)

	containers, err := s.ManagedContainers()
	if err != nil {
		return nil, err
	}

	registrations := []*config.ServiceRegistration{}

	for _, container := range containers {
		name := s.EnvFor(container)["GALAXY_APP"]

		registration, err := s.configStore.RegisterService(env, pool, hostIP, container)
		if err != nil {
			log.Printf("ERROR: Could not register %s: %s\n", name, err.Error())
			continue
		}
		registrations = append(registrations, registration)
	}

	return registrations, nil

}
Beispiel #13
0
func poolDelete(c *cli.Context) {
	ensureEnvArg(c)
	ensurePoolArg(c)
	initStore(c)
	empty, err := configStore.DeletePool(utils.GalaxyPool(c), utils.GalaxyEnv(c))
	if err != nil {
		log.Fatalf("ERROR: Could not delete pool: %s", err)
		return
	}

	if empty {
		log.Printf("Pool %s deleted\n", utils.GalaxyPool(c))

	} else {
		log.Printf("Pool %s has apps assigned. Unassign them first.\n", utils.GalaxyPool(c))
	}
}
Beispiel #14
0
func AppUnassign(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)
	}

	deleted, err := configStore.UnassignApp(app, env, pool)
	if err != nil {
		return err
	}

	if deleted {
		log.Printf("Unassigned %s in env %s from pool %s\n", app, env, pool)
	} else {
		log.Printf("%s could not be unassigned.\n", pool)
	}
	return nil
}
Beispiel #15
0
// return --base, or try to find a base cloudformation stack
func getBase(c *cli.Context) string {
	errNoBase := fmt.Errorf("could not identify a unique base stack")

	base := c.String("base")
	if base != "" {
		return base
	}

	descResp, err := stack.DescribeStacks("")
	if err != nil {
		log.Fatal(err)
	}

	for _, stack := range descResp.Stacks {
		// first check for galaxy:base tag
		baseTag := false
		for _, t := range stack.Tags {
			if t.Key == "galaxy" && t.Value == "base" {
				baseTag = true
			}
		}
		if baseTag {
			if base != "" {
				err = errNoBase
			}
			base = stack.Name
			continue
		}
		parts := strings.Split(stack.Name, "-")

		// check for "-base" in the name
		if parts[len(parts)-1] == "base" {
			if base != "" {
				err = errNoBase
			}
			base = stack.Name
			continue
		}

		// the best we can do for now is look for a stack with a single word
		if len(parts) == 1 {
			if base != "" {
				err = errNoBase
			}
			base = stack.Name

			log.Printf("Warning: guessing base stack: %s", base)
		}
	}

	if err != nil {
		log.Fatalf("%s: %s", err, "use --base")
	}

	return base
}
Beispiel #16
0
func pullImage(appCfg config.App) (*docker.Image, error) {
	image, err := serviceRuntime.PullImage(appCfg.Version(), appCfg.VersionID())
	if image == nil || err != nil {
		log.Errorf("ERROR: Could not pull image %s: %s", appCfg.Version(), err)
		return nil, err
	}

	log.Printf("Pulled %s version %s\n", appCfg.Name(), appCfg.Version())
	return image, nil
}
Beispiel #17
0
func AppCreate(configStore *config.Store, app, env string) error {
	// Don't allow creating runtime hosts entries
	if app == "hosts" {
		return fmt.Errorf("could not create app: %s", app)
	}

	created, err := configStore.CreateApp(app, env)

	if err != nil {
		return fmt.Errorf("could not create app: %s", err)
	}

	if created {
		log.Printf("Created %s in env %s.\n", app, env)
	} else {
		log.Printf("%s already exists in in env %s.", app, env)
	}
	return nil
}
Beispiel #18
0
func AppDelete(configStore *config.Store, app, env string) error {

	// Don't allow deleting runtime hosts entries
	if app == "hosts" || app == "pools" {
		return fmt.Errorf("could not delete app: %s", app)
	}

	deleted, err := configStore.DeleteApp(app, env)
	if err != nil {
		return fmt.Errorf("could not delete app: %s", err)
	}

	if deleted {
		log.Printf("Deleted %s from env %s.\n", app, env)
	} else {
		log.Printf("%s does not exists in env %s.\n", app, env)
	}
	return nil
}
Beispiel #19
0
// inspectImage checks that the running image matches the config.
// We only use this to print warnings, since we likely need to deploy a new
// config version to fix the inconsistency.
func inspectImage(appCfg config.App) {
	image, err := serviceRuntime.InspectImage(appCfg.Version())
	if err != nil {
		log.Println("error inspecting image", appCfg.Version())
		return
	}

	if utils.StripSHA(image.ID) != appCfg.VersionID() {
		log.Printf("warning: %s image ID does not match config", appCfg.Name())
	}
}
Beispiel #20
0
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
}
Beispiel #21
0
func (s *ServiceRuntime) UnRegisterAll(env, pool, hostIP string) ([]*docker.Container, error) {

	containers, err := s.ManagedContainers()
	if err != nil {
		return nil, err
	}

	removed := []*docker.Container{}

	for _, container := range containers {
		name := s.EnvFor(container)["GALAXY_APP"]
		_, err = s.configStore.UnRegisterService(env, pool, hostIP, container)
		if err != nil {
			log.Printf("ERROR: Could not unregister %s: %s\n", name, err)
			return removed, err
		}

		removed = append(removed, container)
		log.Printf("Unregistered %s as %s", container.ID[0:12], name)
	}

	return removed, nil
}
Beispiel #22
0
func startService(appCfg config.App, logStatus bool) {

	desired, err := commander.Balanced(configStore, hostIP, appCfg.Name(), env, pool)
	if err != nil {
		log.Errorf("ERROR: Could not determine instance count: %s", err)
		return
	}

	running, err := serviceRuntime.InstanceCount(appCfg.Name(), strconv.FormatInt(appCfg.ID(), 10))
	if err != nil {
		log.Errorf("ERROR: Could not determine running instance count: %s", err)
		return
	}

	for i := 0; i < desired-running; i++ {
		container, err := serviceRuntime.Start(env, pool, appCfg)
		if err != nil {
			log.Errorf("ERROR: Could not start containers: %s", err)
			return
		}

		log.Printf("Started %s version %s as %s\n", appCfg.Name(), appCfg.Version(), container.ID[0:12])

		err = serviceRuntime.StopOldVersion(appCfg, 1)
		if err != nil {
			log.Errorf("ERROR: Could not stop containers: %s", err)
		}
	}

	running, err = serviceRuntime.InstanceCount(appCfg.Name(), strconv.FormatInt(appCfg.ID(), 10))
	if err != nil {
		log.Errorf("ERROR: Could not determine running instance count: %s", err)
		return
	}

	for i := 0; i < running-desired; i++ {
		err := serviceRuntime.Stop(appCfg)
		if err != nil {
			log.Errorf("ERROR: Could not stop container: %s", err)
		}
	}

	err = serviceRuntime.StopOldVersion(appCfg, -1)
	if err != nil {
		log.Errorf("ERROR: Could not stop old containers: %s", err)
	}

	// check the image version, and log any inconsistencies
	inspectImage(appCfg)
}
Beispiel #23
0
func (s *ServiceRuntime) StopOldVersion(appCfg config.App, limit int) error {
	containers, err := s.ManagedContainers()
	if err != nil {
		return err
	}

	stopped := 0

	for _, container := range containers {

		if stopped == limit {
			return nil
		}

		env := s.EnvFor(container)
		// Container name does match one that would be started w/ this service config
		if env["GALAXY_APP"] != appCfg.Name() {
			continue
		}

		image, err := s.InspectImage(container.Image)
		if err != nil {
			log.Errorf("ERROR: Unable to inspect image: %s", container.Image)
			continue
		}

		if image == nil {
			log.Errorf("ERROR: Image for container %s does not exist!", container.ID[0:12])
			continue

		}

		version := env["GALAXY_VERSION"]

		if version == "" {
			log.Printf("WARNING: %s missing GALAXY_VERSION", appCfg.ContainerName())
		}

		if version != strconv.FormatInt(appCfg.ID(), 10) && version != "" {
			s.stopContainer(container)
			stopped = stopped + 1
		}
	}

	return nil
}
Beispiel #24
0
// FIXME: This can't be shut down. Make sure that's not a problem
func (c *ConsulBackend) sub(key string, msgs chan string) {
	var events []*consul.UserEvent
	var meta *consul.QueryMeta
	var err error
	for {
		// No way to handle failure here, just keep trying to get our first set of events.
		// We need a successful query to get the last index to search from.
		events, meta, err = c.client.Event().List(key, nil)
		if err != nil {
			log.Println("Subscribe error:", err)
			time.Sleep(5 * time.Second)
			continue
		}
		// cache all old events
		c.seen.Filter(events)
		break
	}

	lastIndex := meta.LastIndex
	for {
		opts := &consul.QueryOptions{
			WaitIndex: lastIndex,
			WaitTime:  30 * time.Second,
		}
		events, meta, err = c.client.Event().List(key, opts)
		if err != nil {
			log.Printf("Subscribe(%s): %s\n", key, err.Error())
			continue
		}

		if meta.LastIndex == lastIndex {
			// no new events
			continue
		}

		for _, event := range c.seen.Filter(events) {
			msgs <- string(event.Payload)
		}

		lastIndex = meta.LastIndex
	}
}
Beispiel #25
0
func (s *ServiceRuntime) ManagedContainers() ([]*docker.Container, error) {
	apps := []*docker.Container{}
	containers, err := s.dockerClient.ListContainers(docker.ListContainersOptions{
		All: true,
	})
	if err != nil {
		return apps, err
	}

	for _, c := range containers {
		container, err := s.dockerClient.InspectContainer(c.ID)
		if err != nil {
			log.Printf("ERROR: Unable to inspect container: %s\n", c.ID)
			continue
		}
		name := s.EnvFor(container)["GALAXY_APP"]
		if name != "" && (container.State.Running || container.State.Restarting) {
			apps = append(apps, container)
		}
	}
	return apps, nil
}
Beispiel #26
0
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
}
Beispiel #27
0
// RegisterEvents monitors the docker daemon for events, and returns those
// that require registration action over the listener chan.
func (s *ServiceRuntime) RegisterEvents(env, pool, hostIP string, listener chan ContainerEvent) error {
	go func() {
		c := make(chan *docker.APIEvents)

		watching := false
		for {

			err := s.Ping()
			if err != nil {
				log.Errorf("ERROR: Unable to ping docker daemaon: %s", err)
				if watching {
					s.dockerClient.RemoveEventListener(c)
					watching = false
				}
				time.Sleep(10 * time.Second)
				continue

			}

			if !watching {
				err = s.dockerClient.AddEventListener(c)
				if err != nil && err != docker.ErrListenerAlreadyExists {
					log.Printf("ERROR: Error registering docker event listener: %s", err)
					time.Sleep(10 * time.Second)
					continue
				}
				watching = true
			}

			select {

			case e := <-c:
				if e.Status == "start" || e.Status == "stop" || e.Status == "die" {
					container, err := s.InspectContainer(e.ID)
					if err != nil {
						log.Printf("ERROR: Error inspecting container: %s", err)
						continue
					}

					if container == nil {
						log.Printf("WARN: Nil container returned for %s", e.ID[:12])
						continue
					}

					name := s.EnvFor(container)["GALAXY_APP"]
					if name != "" {
						registration, err := s.configStore.GetServiceRegistration(env, pool, hostIP, container)
						if err != nil {
							log.Printf("WARN: Could not find service registration for %s/%s: %s", name, container.ID[:12], err)
							continue
						}

						if registration == nil && e.Status != "start" {
							continue
						}

						// if a container is restarting, don't continue re-registering the app
						if container.State.Restarting {
							continue
						}

						listener <- ContainerEvent{
							Status:              e.Status,
							Container:           container,
							ServiceRegistration: registration,
						}
					}

				}
			case <-time.After(10 * time.Second):
				// check for docker liveness
			}

		}
	}()
	return nil
}
Beispiel #28
0
func main() {
	flag.Int64Var(&stopCutoff, "cutoff", 10, "Seconds to wait before stopping old containers")
	flag.StringVar(&registryURL, "registry", utils.GetEnv("GALAXY_REGISTRY_URL", "redis://127.0.0.1:6379"), "registry URL")
	flag.StringVar(&env, "env", utils.GetEnv("GALAXY_ENV", ""), "Environment namespace")
	flag.StringVar(&pool, "pool", utils.GetEnv("GALAXY_POOL", ""), "Pool namespace")
	flag.StringVar(&hostIP, "host-ip", "127.0.0.1", "Host IP")
	flag.StringVar(&shuttleAddr, "shuttle-addr", "", "Shuttle API addr (127.0.0.1:9090)")
	flag.StringVar(&dns, "dns", "", "DNS addr to use for containers")
	flag.BoolVar(&debug, "debug", false, "verbose logging")
	flag.BoolVar(&version, "v", false, "display version info")

	flag.Usage = func() {
		println("Usage: commander [options] <command> [<args>]\n")
		println("Available commands are:")
		println("   agent           Runs commander agent")
		println("   app             List all apps")
		println("   app:assign      Assign an app to a pool")
		println("   app:create      Create an app")
		println("   app:deploy      Deploy an app")
		println("   app:delete      Delete an app")
		println("   app:restart     Restart an app")
		println("   app:run         Run a command within an app on this host")
		println("   app:shell       Run a shell within an app on this host")
		println("   app:start       Starts one or more apps")
		println("   app:stop        Stops one or more apps")
		println("   app:unassign    Unassign an app from a pool")
		println("   config          List config for an app")
		println("   config:get      Get config values for an app")
		println("   config:set      Set config values for an app")
		println("   config:unset    Unset config values for an app")
		println("   runtime         List container runtime policies")
		println("   runtime:set     Set container runtime policies")
		println("   hosts           List hosts in an env and pool")
		println("\nOptions:\n")
		flag.PrintDefaults()
	}

	flag.Parse()

	if version {
		fmt.Println(buildVersion)
		return
	}

	log.DefaultLogger = log.New(os.Stdout, "", log.INFO)
	log.DefaultLogger.SetFlags(0)

	if debug {
		log.DefaultLogger.Level = log.DEBUG
	}

	if flag.NArg() < 1 {
		fmt.Println("Need a command")
		flag.Usage()
		os.Exit(1)
	}

	initOrDie()

	switch flag.Args()[0] {
	case "dump":
		if flag.NArg() < 2 {
			fmt.Println("Usage: commander dump ENV")
			os.Exit(1)
		}
		dump(flag.Arg(1))
		return

	case "restore":
		if flag.NArg() < 2 {
			fmt.Println("Usage: commander dump ENV FILE")
			os.Exit(1)
		}
		restore(flag.Arg(1))
		return

	case "agent":
		log.DefaultLogger.SetFlags(golog.LstdFlags)
		loop = true
		agentFs := flag.NewFlagSet("agent", flag.ExitOnError)
		agentFs.Usage = func() {
			println("Usage: commander agent [options]\n")
			println("    Runs commander continuously\n\n")
			println("Options:\n\n")
			agentFs.PrintDefaults()
		}
		agentFs.Parse(flag.Args()[1:])

		ensureEnv()
		ensurePool()

	case "app":
		appFs := flag.NewFlagSet("app", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander app\n")
			println("    List all apps or apps in an environment\n")
			println("Options:\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])
		err := commander.AppList(configStore, env)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return

	case "app:assign":
		appFs := flag.NewFlagSet("app:assign", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander app:assign <app>\n")
			println("    Assign an app to a pool\n")
			println("Options:\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])

		ensureEnv()
		ensurePool()

		if appFs.NArg() != 1 {
			appFs.Usage()
			os.Exit(1)
		}

		err := commander.AppAssign(configStore, appFs.Args()[0], env, pool)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return
	case "app:create":
		appFs := flag.NewFlagSet("app:create", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander app:create <app>\n")
			println("    Create an app in an environment\n")
			println("Options:\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])

		ensureEnv()

		if appFs.NArg() == 0 {
			appFs.Usage()
			os.Exit(1)
		}

		err := commander.AppCreate(configStore, appFs.Args()[0], env)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return

	case "app:delete":
		appFs := flag.NewFlagSet("app:delete", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander app:delete <app>\n")
			println("    Delete an app in an environment\n")
			println("Options:\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])

		ensureEnv()

		if appFs.NArg() == 0 {
			appFs.Usage()
			os.Exit(1)
		}

		err := commander.AppDelete(configStore, appFs.Args()[0], env)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return

	case "app:deploy":
		appFs := flag.NewFlagSet("app:delete", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander app:deploy [-force] <app> <version>\n")
			println("    Deploy an app in an environment\n")
			println("Options:\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])

		ensureEnv()

		if appFs.NArg() != 2 {
			appFs.Usage()
			os.Exit(1)
		}

		err := commander.AppDeploy(configStore, serviceRuntime, appFs.Args()[0], env, appFs.Args()[1])
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return

	case "app:restart":
		appFs := flag.NewFlagSet("app:restart", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander app:restart <app>\n")
			println("    Restart an app in an environment\n")
			println("Options:\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])

		ensureEnv()

		if appFs.NArg() == 0 {
			appFs.Usage()
			os.Exit(1)
		}

		err := commander.AppRestart(configStore, appFs.Args()[0], env)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return

	case "app:run":
		appFs := flag.NewFlagSet("app:run", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander app:run <app> <cmd>\n")
			println("    Restart an app in an environment\n")
			println("Options:\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])

		ensureEnv()

		if appFs.NArg() < 2 {
			appFs.Usage()
			os.Exit(1)
		}

		err := commander.AppRun(configStore, serviceRuntime, appFs.Args()[0], env, appFs.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return

	case "app:shell":
		appFs := flag.NewFlagSet("app:shell", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander app:shell <app>\n")
			println("    Run a shell for an app\n")
			println("Options:\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])

		ensureEnv()
		ensurePool()

		if appFs.NArg() != 1 {
			appFs.Usage()
			os.Exit(1)
		}

		err := commander.AppShell(configStore, serviceRuntime, appFs.Args()[0], env, pool)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return

	case "app:start":

		startFs := flag.NewFlagSet("app:start", flag.ExitOnError)
		startFs.Usage = func() {
			println("Usage: commander app:start [options] [<app>]*\n")
			println("    Starts one or more apps. If no apps are specified, starts all apps.\n")
			println("Options:\n")
			startFs.PrintDefaults()
		}
		startFs.Parse(flag.Args()[1:])

		apps = startFs.Args()

		if len(apps) == 0 {
			acs, err := configStore.ListApps(env)
			if err != nil {
				log.Fatalf("ERROR: Unable to list apps: %s", err)
			}
			for _, ac := range acs {
				apps = append(apps, ac.Name())
			}
		}
		break
	case "app:status":
		// FIXME: undocumented

		statusFs := flag.NewFlagSet("app:status", flag.ExitOnError)
		statusFs.Usage = func() {
			println("Usage: commander app:status [options] [<app>]*\n")
			println("    Lists status of running apps.\n")
			println("Options:\n")
			statusFs.PrintDefaults()
		}
		statusFs.Parse(flag.Args()[1:])

		ensureEnv()
		ensurePool()

		err := discovery.Status(serviceRuntime, configStore, env, pool, hostIP)
		if err != nil {
			log.Fatalf("ERROR: Unable to list app status: %s", err)
		}
		return

	case "app:stop":
		stopFs := flag.NewFlagSet("app:stop", flag.ExitOnError)
		stopFs.Usage = func() {
			println("Usage: commander app:stop [options] [<app>]*\n")
			println("    Stops one or more apps. If no apps are specified, stops all apps.\n")
			println("Options:\n")
			stopFs.PrintDefaults()
		}
		stopFs.Parse(flag.Args()[1:])

		apps = stopFs.Args()

		for _, app := range apps {
			err := serviceRuntime.StopAllMatching(app)
			if err != nil {
				log.Fatalf("ERROR: Unable able to stop all containers: %s", err)
			}
		}
		if len(apps) > 0 {
			return
		}

		err := serviceRuntime.StopAll(env)
		if err != nil {
			log.Fatalf("ERROR: Unable able to stop all containers: %s", err)
		}
		return
	case "app:unassign":
		appFs := flag.NewFlagSet("app:unassign", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander app:unassign <app>\n")
			println("    Unassign an app to a pool\n")
			println("Options:\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])

		ensureEnv()
		ensurePool()

		if appFs.NArg() != 1 {
			appFs.Usage()
			os.Exit(1)
		}

		err := commander.AppUnassign(configStore, appFs.Args()[0], env, pool)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return

	case "hosts":
		hostFs := flag.NewFlagSet("hosts", flag.ExitOnError)
		hostFs.Usage = func() {
			println("Usage: commander hosts\n")
			println("    List hosts in an env and pool\n")
			println("Options:\n")
			hostFs.PrintDefaults()
		}
		err := hostFs.Parse(flag.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: Bad command line options: %s", err)
		}

		ensureEnv()
		ensurePool()

		err = commander.HostsList(configStore, env, pool)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return
	case "config":
		configFs := flag.NewFlagSet("config", flag.ExitOnError)
		usage := "Usage: commander config <app>"
		configFs.Usage = func() {
			println(usage)
			println("    List config values for an app\n")
			println("Options:\n")
			configFs.PrintDefaults()
		}
		err := configFs.Parse(flag.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: Bad command line options: %s", err)
		}

		ensureEnv()

		if configFs.NArg() != 1 {
			log.Error("ERROR: Missing app name argument")
			log.Printf("Usage: %s", usage)
			os.Exit(1)
		}
		app := configFs.Args()[0]

		err = commander.ConfigList(configStore, app, env)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return
	case "config:get":
		configFs := flag.NewFlagSet("config:get", flag.ExitOnError)
		configFs.Usage = func() {
			println("Usage: commander config <app> KEY [KEY]*\n")
			println("    Get config values for an app\n")
			println("Options:\n")
			configFs.PrintDefaults()
		}
		err := configFs.Parse(flag.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: Bad command line options: %s", err)
		}

		ensureEnv()

		if configFs.NArg() == 0 {
			log.Errorf("ERROR: Missing app name")
			configFs.Usage()
			os.Exit(1)
		}
		app := configFs.Args()[0]

		err = commander.ConfigGet(configStore, app, env, configFs.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return
	case "config:set":
		configFs := flag.NewFlagSet("config:set", flag.ExitOnError)
		configFs.Usage = func() {
			println("Usage: commander config <app> KEY=VALUE [KEY=VALUE]*\n")
			println("    Set config values for an app\n")
			println("Options:\n")
			configFs.PrintDefaults()
		}
		err := configFs.Parse(flag.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: Bad command line options: %s", err)
		}

		ensureEnv()

		if configFs.NArg() == 0 {
			log.Errorf("ERROR: Missing app name")
			configFs.Usage()
			os.Exit(1)
		}
		app := configFs.Args()[0]

		err = commander.ConfigSet(configStore, app, env, configFs.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return
	case "config:unset":
		configFs := flag.NewFlagSet("config:unset", flag.ExitOnError)
		configFs.Usage = func() {
			println("Usage: commander config <app> KEY [KEY]*\n")
			println("    Unset config values for an app\n")
			println("Options:\n")
			configFs.PrintDefaults()
		}
		err := configFs.Parse(flag.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: Bad command line options: %s", err)
		}

		ensureEnv()

		if configFs.NArg() == 0 {
			log.Errorf("ERROR: Missing app name")
			configFs.Usage()
			os.Exit(1)
		}
		app := configFs.Args()[0]

		err = commander.ConfigUnset(configStore, app, env, configFs.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return

	case "runtime":
		runtimeFs := flag.NewFlagSet("runtime", flag.ExitOnError)
		runtimeFs.Usage = func() {
			println("Usage: commander runtime\n")
			println("    List container runtime policies\n")
			println("Options:\n")
			runtimeFs.PrintDefaults()
		}
		err := runtimeFs.Parse(flag.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: Bad command line options: %s", err)
		}

		app := ""
		if runtimeFs.NArg() > 0 {
			app = runtimeFs.Args()[0]
		}

		err = commander.RuntimeList(configStore, app, env, pool)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}
		return

	case "runtime:set":
		var ps int
		var m string
		var c string
		var vhost string
		var port string
		var maint string
		runtimeFs := flag.NewFlagSet("runtime:set", flag.ExitOnError)
		runtimeFs.IntVar(&ps, "ps", 0, "Number of instances to run across all hosts")
		runtimeFs.StringVar(&m, "m", "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
		runtimeFs.StringVar(&c, "c", "", "CPU shares (relative weight)")
		runtimeFs.StringVar(&vhost, "vhost", "", "Virtual host for HTTP routing")
		runtimeFs.StringVar(&port, "port", "", "Service port for service discovery")
		runtimeFs.StringVar(&maint, "maint", "", "Enable or disable maintenance mode")

		runtimeFs.Usage = func() {
			println("Usage: commander runtime:set [-ps 1] [-m 100m] [-c 512] [-vhost x.y.z] [-port 8000] [-maint false] <app>\n")
			println("    Set container runtime policies\n")
			println("Options:\n")
			runtimeFs.PrintDefaults()
		}

		err := runtimeFs.Parse(flag.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: Bad command line options: %s", err)
		}

		ensureEnv()

		if ps != 0 || m != "" || c != "" || maint != "" {
			ensurePool()
		}

		if runtimeFs.NArg() != 1 {
			runtimeFs.Usage()
			os.Exit(1)
		}

		app := runtimeFs.Args()[0]

		_, err = utils.ParseMemory(m)
		if err != nil {
			log.Fatalf("ERROR: Bad memory option %s: %s", m, err)
		}

		updated, err := commander.RuntimeSet(configStore, app, env, pool, commander.RuntimeOptions{
			Ps:              ps,
			Memory:          m,
			CPUShares:       c,
			VirtualHost:     vhost,
			Port:            port,
			MaintenanceMode: maint,
		})
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}

		if !updated {
			log.Fatalf("ERROR: Failed to set runtime options.")
		}

		if pool != "" {
			log.Printf("Runtime options updated for %s in %s running on %s", app, env, pool)
		} else {
			log.Printf("Runtime options updated for %s in %s", app, env)
		}
		return

	case "runtime:unset":
		var ps, m, c, port bool
		var vhost string
		runtimeFs := flag.NewFlagSet("runtime:unset", flag.ExitOnError)
		runtimeFs.BoolVar(&ps, "ps", false, "Number of instances to run across all hosts")
		runtimeFs.BoolVar(&m, "m", false, "Memory limit")
		runtimeFs.BoolVar(&c, "c", false, "CPU shares (relative weight)")
		runtimeFs.StringVar(&vhost, "vhost", "", "Virtual host for HTTP routing")
		runtimeFs.BoolVar(&port, "port", false, "Service port for service discovery")

		runtimeFs.Usage = func() {
			println("Usage: commander runtime:unset [-ps] [-m] [-c] [-vhost x.y.z] [-port] <app>\n")
			println("    Reset and removes container runtime policies to defaults\n")
			println("Options:\n")
			runtimeFs.PrintDefaults()
		}

		err := runtimeFs.Parse(flag.Args()[1:])
		if err != nil {
			log.Fatalf("ERROR: Bad command line options: %s", err)
		}

		ensureEnv()

		if ps || m || c {
			ensurePool()
		}

		if runtimeFs.NArg() != 1 {
			runtimeFs.Usage()
			os.Exit(1)
		}

		app := runtimeFs.Args()[0]

		options := commander.RuntimeOptions{
			VirtualHost: vhost,
		}
		if ps {
			options.Ps = -1
		}

		if m {
			options.Memory = "-"
		}

		if c {
			options.CPUShares = "-"
		}

		if port {
			options.Port = "-"
		}

		updated, err := commander.RuntimeUnset(configStore, app, env, pool, options)
		if err != nil {
			log.Fatalf("ERROR: %s", err)
		}

		if !updated {
			log.Fatalf("ERROR: Failed to set runtime options.")
		}

		if pool != "" {
			log.Printf("Runtime options updated for %s in %s running on %s", app, env, pool)
		} else {
			log.Printf("Runtime options updated for %s in %s", app, env)
		}
		return

	case "pool":

		err := commander.ListPools(configStore, env)
		if err != nil {
			log.Fatal(err)
		}
		return

	case "pool:create":
		appFs := flag.NewFlagSet("pool:create", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander -env <env> pool:create <pool>\n")
			println("    Create a pool in <env>\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])

		ensureEnv()

		if pool == "" && appFs.NArg() > 0 {
			pool = appFs.Arg(0)
		} else {
			ensurePool()
		}

		err := commander.PoolCreate(configStore, env, pool)
		if err != nil {
			log.Fatalf("ERROR: Could not create pool: %s", err)
		}
		fmt.Println("created pool:", pool)
		return

	case "pool:delete":
		appFs := flag.NewFlagSet("pool:delete", flag.ExitOnError)
		appFs.Usage = func() {
			println("Usage: commander -env <env> pool:delete <pool>\n")
			println("    Delete a pool from <env>\n")
			appFs.PrintDefaults()
		}
		appFs.Parse(flag.Args()[1:])

		ensureEnv()

		if pool == "" && flag.NArg() > 1 {
			pool = flag.Arg(1)
		} else {
			ensurePool()
		}

		err := commander.PoolDelete(configStore, env, pool)
		if err != nil {
			log.Fatalf("ERROR: Could not delete pool: %s", err)
			return
		}

		fmt.Println("deleted pool:", pool)
		return

	default:
		fmt.Println("Unknown command")
		flag.Usage()
		os.Exit(1)
	}

	ensureEnv()
	ensurePool()

	log.Printf("Starting commander %s", buildVersion)
	log.Printf("env=%s pool=%s host-ip=%s registry=%s shuttle-addr=%s dns=%s cutoff=%ds",
		env, pool, hostIP, registryURL, shuttleAddr, dns, stopCutoff)

	defer func() {
		configStore.DeleteHost(env, pool, config.HostInfo{
			HostIP: hostIP,
		})
	}()

	for app, ch := range workerChans {
		if len(apps) == 0 || utils.StringInSlice(app, apps) {
			wg.Add(1)
			go restartContainers(app, ch)
			ch <- "deploy"
		}
	}

	if loop {
		wg.Add(1)
		go heartbeatHost()

		go discovery.Register(serviceRuntime, configStore, env, pool, hostIP, shuttleAddr)
		cancelChan := make(chan struct{})
		// do we need to cancel ever?

		restartChan := configStore.Watch(env, cancelChan)
		monitorService(restartChan)
	}

	// TODO: do we still need a WaitGroup?
	wg.Wait()
}
Beispiel #29
0
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
}
Beispiel #30
0
func (s *ServiceRuntime) RunCommand(env string, appCfg config.App, cmd []string) (*docker.Container, 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 nil, err
	}

	instanceId, err := s.NextInstanceSlot(appCfg.Name(), strconv.FormatInt(appCfg.ID(), 10))
	if err != nil {
		return nil, err
	}

	envVars := []string{"ENV=" + env}

	for key, value := range appCfg.Env() {
		if key == "ENV" {
			continue
		}
		envVars = append(envVars, strings.ToUpper(key)+"="+s.replaceVarEnv(value, s.hostIP))
	}
	envVars = append(envVars, "GALAXY_APP="+appCfg.Name())
	envVars = append(envVars, "GALAXY_VERSION="+strconv.FormatInt(appCfg.ID(), 10))
	envVars = append(envVars, fmt.Sprintf("GALAXY_INSTANCE=%s", strconv.FormatInt(int64(instanceId), 10)))

	runCmd := []string{"/bin/bash", "-c", strings.Join(cmd, " ")}

	container, err := s.dockerClient.CreateContainer(docker.CreateContainerOptions{
		Config: &docker.Config{
			Image:        appCfg.Version(),
			Env:          envVars,
			AttachStdout: true,
			AttachStderr: true,
			Cmd:          runCmd,
			OpenStdin:    false,
		},
	})

	if err != nil {
		return nil, err
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	go func(s *ServiceRuntime, containerId string) {
		<-c
		log.Println("Stopping container...")
		err := s.dockerClient.StopContainer(containerId, 3)
		if err != nil {
			log.Printf("ERROR: Unable to stop container: %s", err)
		}
		err = s.dockerClient.RemoveContainer(docker.RemoveContainerOptions{
			ID: containerId,
		})
		if err != nil {
			log.Printf("ERROR: Unable to stop container: %s", err)
		}

	}(s, container.ID)

	defer s.dockerClient.RemoveContainer(docker.RemoveContainerOptions{
		ID: container.ID,
	})
	config := &docker.HostConfig{}
	if s.dns != "" {
		config.DNS = []string{s.dns}
	}
	err = s.dockerClient.StartContainer(container.ID, config)

	if err != nil {
		return container, err
	}

	// FIXME: Hack to work around the race of attaching to a container before it's
	// actually running.  Tried polling the container and then attaching but the
	// output gets lost sometimes if the command executes very quickly. Not sure
	// what's going on.
	time.Sleep(1 * time.Second)

	err = s.dockerClient.AttachToContainer(docker.AttachToContainerOptions{
		Container:    container.ID,
		OutputStream: os.Stdout,
		ErrorStream:  os.Stderr,
		Logs:         true,
		Stream:       false,
		Stdout:       true,
		Stderr:       true,
	})

	if err != nil {
		log.Printf("ERROR: Unable to attach to running container: %s", err.Error())
	}

	s.dockerClient.WaitContainer(container.ID)

	return container, err
}