예제 #1
0
func (e *RemoveDependerEnvDataForDependerAppExecutor) Execute(t *Task) error {
	if e.arg.App == "" {
		return errors.New("Please specify an app")
	}
	if e.arg.Depender == "" {
		return errors.New("Please specify a depender app")
	}
	if e.arg.Env == "" {
		return errors.New("Please specify an env")
	}
	zkApp, err := datamodel.GetApp(e.arg.App)
	if err != nil {
		e.reply.Status = StatusError
		return err
	}
	err = zkApp.RemoveDependerEnvDataForDependerApp(e.arg.Depender, e.arg.Env)
	if err != nil {
		e.reply.Status = StatusError
		return err
	}
	e.reply.Status = StatusOk
	castedApp := App(*zkApp)
	e.reply.App = &castedApp
	return err
}
예제 #2
0
func (e *RegisterAppExecutor) Execute(t *Task) error {
	if e.arg.Name == "" {
		return errors.New("Please specify an app name to register")
	}
	if !AppRegexp.MatchString(e.arg.Name) {
		return errors.New("App name must be [A-Za-z0-9-]+")
	}
	if !e.arg.NonAtlantis && e.arg.Repo == "" {
		return errors.New("Please specify a repo")
	}
	if !e.arg.NonAtlantis && e.arg.Root == "" {
		return errors.New("Please specify the repo's root")
	}
	if e.arg.Email == "" {
		return errors.New("Please specify the email of the app owner")
	}
	if _, err := datamodel.GetApp(e.arg.Name); err == nil {
		return errors.New("Already Registered.")
	}
	_, err := datamodel.CreateOrUpdateApp(e.arg.NonAtlantis, e.arg.Internal, e.arg.Name, e.arg.Repo, e.arg.Root,
		e.arg.Email)
	if err != nil {
		e.reply.Status = StatusError
	}
	e.reply.Status = StatusOk
	return err
}
예제 #3
0
func (e *AddDependerEnvDataForDependerAppExecutor) Execute(t *Task) error {
	if e.arg.App == "" {
		return errors.New("Please specify an app")
	}
	if e.arg.Depender == "" {
		return errors.New("Please specify a depender app")
	}
	if e.arg.DependerEnvData == nil {
		return errors.New("Please specify data for the env")
	} else if e.arg.DependerEnvData.Name == "" {
		return errors.New("Please specify name for the env")
	}
	// verify SecurityGroups are valid
	for ipGroup, _ := range e.arg.DependerEnvData.SecurityGroup {
		if _, err := datamodel.GetIPGroup(ipGroup); err != nil {
			return errors.New("Invalid IP Group in Security Group: " + ipGroup)
		}
	}
	zkApp, err := datamodel.GetApp(e.arg.App)
	if err != nil {
		e.reply.Status = StatusError
		return err
	}
	err = zkApp.AddDependerEnvDataForDependerApp(e.arg.Depender, e.arg.DependerEnvData)
	if err != nil {
		e.reply.Status = StatusError
		return err
	}
	e.reply.Status = StatusOk
	castedApp := App(*zkApp)
	e.reply.App = &castedApp
	return err
}
예제 #4
0
func (e *GetAppEnvPortExecutor) Execute(t *Task) (err error) {
	if e.arg.App == "" {
		return errors.New("Please specify an app")
	} else if e.arg.Env == "" {
		return errors.New("Please specify an environment")
	}
	zkApp, err := datamodel.GetApp(e.arg.App)
	if err != nil {
		return err
	}
	zrp := datamodel.GetRouterPorts(zkApp.Internal)
	fmt.Printf("ROUTER PORTS: %+v\n", zrp)
	portStr := zrp.AppEnvMap[helper.GetAppEnvTrieName(e.arg.App, e.arg.Env)]
	if portStr == "" {
		return errors.New("port not found")
	}

	fmt.Printf("PORT STRING: %+v -> %+v\n", helper.GetAppEnvTrieName(e.arg.App, e.arg.Env), portStr)
	port, err := strconv.ParseUint(portStr, 10, 16)
	if err != nil {
		return err
	}
	helper.SetRouterRoot(zkApp.Internal)
	e.reply.Port, err = routerzk.GetPort(datamodel.Zk.Conn, uint16(port))
	return err
}
예제 #5
0
func (e *GetAppExecutor) Execute(t *Task) error {
	if e.arg.Name == "" {
		return errors.New("Please specify an app name to get")
	}
	app, err := datamodel.GetApp(e.arg.Name)
	if err != nil || app == nil {
		e.reply.Status = StatusError
		return errors.New("App " + e.arg.Name + " does not exist")
	}
	e.reply.Status = StatusOk
	castedApp := App(*app)
	e.reply.App = &castedApp
	return nil
}
예제 #6
0
func (e *UnregisterAppExecutor) Execute(t *Task) error {
	if e.arg.Name == "" {
		return errors.New("Please specify an app name to unregister")
	}
	app, err := datamodel.GetApp(e.arg.Name)
	if err != nil || app == nil {
		e.reply.Status = StatusError
		return errors.New("App " + e.arg.Name + " does not exist")
	}
	if err = app.Delete(); err != nil {
		e.reply.Status = StatusError
		return err
	}
	e.reply.Status = StatusOk
	return nil
}
예제 #7
0
func (e *GetDependerEnvDataExecutor) Execute(t *Task) error {
	if e.arg.App == "" {
		return errors.New("Please specify an app")
	}
	if e.arg.Env == "" {
		return errors.New("Please specify an env")
	}
	zkApp, err := datamodel.GetApp(e.arg.App)
	if err != nil {
		e.reply.Status = StatusError
		return err
	}
	data := zkApp.GetDependerEnvData(e.arg.Env, true)
	if err != nil {
		e.reply.Status = StatusError
		return err
	}
	e.reply.Status = StatusOk
	e.reply.DependerEnvData = data
	return err
}
예제 #8
0
func (e *RequestAppDependencyExecutor) Execute(t *Task) error {
	if e.arg.App == "" {
		e.reply.Status = StatusError
		return errors.New("Please specify an app")
	}
	if e.arg.Dependency == "" {
		e.reply.Status = StatusError
		return errors.New("Please specify an app to depend on")
	}
	if len(e.arg.Envs) == 0 {
		e.reply.Status = StatusError
		return errors.New("Please specify the envs your app needs the dependency in")
	}
	for _, env := range e.arg.Envs {
		if _, err := datamodel.GetEnv(env); err != nil {
			e.reply.Status = StatusError
			return errors.New("The env " + env + " does not exist")
		}
	}
	// fetch apps
	zkApp, err := datamodel.GetApp(e.arg.App)
	if err != nil {
		e.reply.Status = StatusError
		return err
	}
	zkDep, err := datamodel.GetApp(e.arg.Dependency)
	if err != nil {
		e.reply.Status = StatusError
		return err
	}
	// check depender envs
	missingEnvs := []string{}
	if dad := zkDep.GetDependerAppData(e.arg.App, false); dad != nil {
		for _, env := range e.arg.Envs {
			if dad.DependerEnvData[env] == nil {
				missingEnvs = append(missingEnvs, env)
			}
		}
	} else {
		missingEnvs = e.arg.Envs
	}
	if len(missingEnvs) == 0 {
		return errors.New(fmt.Sprintf("Your app already has access to the dependency %s in envs %v",
			e.arg.Dependency, e.arg.Envs))
	}

	// load template, format body, and set up subject
	subject := fmt.Sprintf("[Atlantis] '%s' is requesting '%s' as a dependency in envs %s", e.arg.App,
		e.arg.Dependency, strings.Join(e.arg.Envs, ","))

	tmpl := template.Must(template.New("request_dependency").Parse(`
{{.DepTeam}},

{{.User}} has requested that you add '{{.App}}' as a depender of your app '{{.Dependency}}' in the the environment(s) '{{.Envs}}'.

Please visit this page to do so: https://{{.ManagerCName}}/static/dashboard/#addAppDepender/{{.Dependency}}/{{.App}}/{{.Envs}}

If you wish to discuss this dependency, {{.AppTeam}} is included in this thread for your convenience.
`))
	myself, err := datamodel.GetManager(Region, Host)
	if err != nil {
		return err
	}
	buf := bytes.NewBuffer([]byte{})
	tmpl.Execute(buf, RequestAppDependencyTemplate{
		App:          e.arg.App,
		AppTeam:      strings.SplitN(zkApp.Email, "@", 2)[0],
		Dependency:   e.arg.Dependency,
		DepTeam:      strings.SplitN(zkDep.Email, "@", 2)[0],
		Envs:         strings.Join(e.arg.Envs, ","),
		ManagerCName: myself.ManagerCName,
		User:         e.arg.ManagerAuthArg.User,
	})
	// send email requesting dependency
	if err := smtp.SendMail([]string{zkDep.Email, zkApp.Email}, subject, buf.String()); err != nil {
		e.reply.Status = StatusError
		return err
	}
	e.reply.Status = StatusOk
	return nil
}
예제 #9
0
func (e *DeployExecutor) Execute(t *Task) error {
	// error checking
	if e.arg.App == "" {
		return errors.New("Please specify an app")
	}
	if e.arg.Sha == "" {
		return errors.New("Please specify a sha")
	}
	if e.arg.Env == "" {
		return errors.New("Please specify an environment")
	}
	if e.arg.CPUShares < 0 ||
		(e.arg.CPUShares > 0 && e.arg.CPUShares != 1 && e.arg.CPUShares%CPUSharesIncrement != 0) {
		return errors.New(fmt.Sprintf("CPU Shares should be 1 or a multiple of %d", CPUSharesIncrement))
	}
	if e.arg.MemoryLimit < 0 ||
		(e.arg.MemoryLimit > 0 && e.arg.MemoryLimit%MemoryLimitIncrement != 0) {
		return errors.New(fmt.Sprintf("Memory should be a multiple of %d", MemoryLimitIncrement))
	}
	// fetch the repo and root
	app, err := datamodel.GetApp(e.arg.App)
	if err != nil {
		return errors.New("App " + e.arg.App + " is not registered: " + err.Error())
	}
	// fetch and parse manifest for app name
	manifestReader, err := builder.DefaultBuilder.Build(t, app.Repo, app.Root, e.arg.Sha)
	if err != nil {
		return errors.New("Build Error: " + err.Error())
	}
	defer manifestReader.Close()
	t.LogStatus("Reading Manifest")
	data, err := bman.Read(manifestReader)
	if err != nil {
		return err
	}
	manifest, err := CreateManifest(data)
	if err != nil {
		return err
	}
	if e.arg.App != manifest.Name {
		// NOTE(edanaher): If we kick off two jobs simultaneously, they will assume they have the same job id, so
		// one of them will get the manifest from the other one's Jenkins job.  Unfortunately, Jenkins doesn't
		// give back any sort of useful information when you create the job, so we can't just use an ID easily.
		// Moreover, the API may not in any way acknowledge the job for several seconds, meaning that we can't
		// even scan the jobs created during a brief time interval for the job in question.  Rather, we have to
		// poll Jenkins until we find the job we're looking for.  This is sufficiently terrible that I'm just
		// erroring out and blaming Jenkins rather than adding a giant pile of code to handle that case.  We could
		// retry the job ourself, but after a day trying to beat Jenkins into submission, I have no interest in
		// applying further hacks.
		return errors.New("The app name you specified does not match the manifest.  This is probably due to an unavoidable race condition in Jenkin's RESTless API.  Please try again.")
	}
	if e.arg.CPUShares > 0 {
		manifest.CPUShares = e.arg.CPUShares
	}
	if e.arg.MemoryLimit > 0 {
		manifest.MemoryLimit = e.arg.MemoryLimit
	}
	// figure out how many instances we need
	if e.arg.Instances > 0 {
		manifest.Instances = e.arg.Instances
	} else if manifest.Instances == 0 {
		manifest.Instances = uint(1) // default to 1 instance
	}
	if e.arg.Dev {
		e.reply.Containers, err = devDeploy(&e.arg.ManagerAuthArg, manifest, e.arg.Sha, e.arg.Env, t)
	} else {
		e.reply.Containers, err = deploy(&e.arg.ManagerAuthArg, manifest, e.arg.Sha, e.arg.Env, t)
	}
	return err
}
예제 #10
0
func ResolveDepValuesForZone(app string, zkEnv *datamodel.ZkEnv, zone string, names []string, encrypt bool, t *Task) (DepsType, error) {
	var (
		err    error
		suffix string
	)
	deps := DepsType{}
	// if we're using DNS and the app is registered, try to get the app cname (if deployed)
	if dns.Provider != nil {
		suffix, err = dns.Provider.Suffix(Region)
		if err != nil {
			return deps, err
		}
	}
	for _, name := range names {
		// if app is registered for this dependency name
		zkApp, err := datamodel.GetApp(name)
		if err != nil {
			continue
		}
		appEnvData := zkApp.GetDependerEnvDataForDependerApp(app, zkEnv.Name, true)
		if appEnvData == nil {
			continue
		}
		envData := zkApp.GetDependerEnvData(zkEnv.Name, true)
		if envData == nil {
			envData = &DependerEnvData{Name: appEnvData.Name}
		}
		// merge the data
		mergedEnvData := MergeDependerEnvData(envData, appEnvData)
		appDep := &AppDep{
			SecurityGroup: mergedEnvData.SecurityGroup,
			DataMap:       mergedEnvData.DataMap,
		}
		if dns.Provider != nil && !zkApp.NonAtlantis && zkApp.Internal {
			// auto-populate Address
			port, created, err := datamodel.ReserveRouterPortAndUpdateTrie(zkApp.Internal, name, "", zkEnv.Name)
			if err != nil {
				return deps, err
			}
			if created {
				// add warning since this means that the app has not been deployed in this env yet
				t.AddWarning("App dependency " + name + " has not yet been deployed in environment " + zkEnv.Name)
			}
			if appDep.DataMap == nil {
				appDep.DataMap = map[string]interface{}{}
			}
			appDep.DataMap["address"] = helper.GetZoneRouterCName(true, zone, suffix) + ":" + port

			// auto-populate SecurityGroup
			portUint, err := strconv.ParseUint(port, 10, 16)
			if err != nil {
				return deps, err
			}
			appDep.SecurityGroup = map[string][]uint16{netsec.InternalRouterIPGroup: []uint16{uint16(portUint)}}
		}
		deps[name] = appDep
	}
	if encrypt {
		for _, value := range deps {
			crypto.EncryptAppDep(value)
		}
	}
	for _, name := range names {
		if _, ok := deps[name]; !ok {
			return deps, errors.New("Could not resolve dep " + name)
		}
	}
	return deps, nil
}
예제 #11
0
func deployToHostsInZones(deps map[string]DepsType, manifest *Manifest, sha, env string,
	hosts map[string][]string, zones []string, t *Task) ([]*Container, error) {
	deployedContainers := []*Container{}
	// fetch the app
	zkApp, err := datamodel.GetApp(manifest.Name)
	if err != nil {
		return nil, err
	}
	// first check if zones have enough hosts
	for _, zone := range zones {
		// fail if zone has no hosts
		if hosts[zone] == nil || len(hosts[zone]) == 0 {
			return nil, errors.New(fmt.Sprintf("No hosts available for app %s in zone %s", manifest.Name, zone))
		}
	}
	// now that we know that enough hosts are available
	t.LogStatus("Deploying to zones: %v", zones)
	respCh := make(chan *DeployZoneResult, len(zones))
	for _, zone := range zones {
		go deployToZone(respCh, deps, manifest, sha, env, hosts[zone], zone)
	}
	numResults := 0
	status := "Deployed to zones: "
	for result := range respCh {
		deployedContainers = append(deployedContainers, result.Containers...)
		if result.Error != nil {
			err = result.Error
			t.Log(err.Error())
			status += result.Zone + ":FAIL "
		} else {
			status += result.Zone + ":SUCCESS "
		}
		t.LogStatus(status)
		numResults++
		if numResults >= len(zones) { // we're done
			close(respCh)
		}
	}
	if err != nil {
		cleanup(false, deployedContainers, t)
		return nil, err
	}

	// set ports on zk supervisor - can't do this in parallel. we may deploy to the same host at the same time
	// and since we don't lock zookeeper (maybe we should), this would result in a race condition.
	t.LogStatus("Updating Zookeeper")
	for _, cont := range deployedContainers {
		datamodel.Supervisor(cont.Host).SetContainerAndPort(cont.ID, cont.PrimaryPort)
	}

	// we're good now, so lets move on
	t.LogStatus("Updating Router")
	deployedIDs := make([]string, len(deployedContainers))
	count := 0
	for _, cont := range deployedContainers {
		deployedIDs[count] = cont.ID
		count++
	}
	err = datamodel.AddToPool(deployedIDs)
	if err != nil { // if we can't add the pool, clean up and fail
		cleanup(true, deployedContainers, t)
		return nil, errors.New("Update Pool Error: " + err.Error())
	}
	if zkApp.Internal {
		// reserve router port if needed and add app+env
		_, _, err = datamodel.ReserveRouterPortAndUpdateTrie(zkApp.Internal, manifest.Name, sha, env)
		if err != nil {
			datamodel.DeleteFromPool(deployedIDs)
			cleanup(true, deployedContainers, t)
			return nil, errors.New("Reserve Router Port Error: " + err.Error())
		}
	} else {
		// only update trie
		_, err = datamodel.UpdateAppEnvTrie(zkApp.Internal, manifest.Name, sha, env)
		if err != nil {
			datamodel.DeleteFromPool(deployedIDs)
			cleanup(true, deployedContainers, t)
			return nil, errors.New("Reserve Router Port Error: " + err.Error())
		}
	}
	return deployedContainers, nil
}