func Run(overrideEnvVarName string, target string, templaterDir string, continueOnError bool) { attrMerger, err := merger.NewAttributesMerger(templaterDir + pathAttributes) if err != nil { logs.WithE(err).Warn("Failed to prepare attributes") } attributes := attrMerger.Merge() attributes = overrideWithJsonIfNeeded(overrideEnvVarName, attributes) tt, err := merger.ProcessAttributesTemplating(attributes, attributes) attributes = tt.(map[string]interface{}) if err != nil { logs.WithField("dir", templaterDir+pathTemplates).Fatal("Failed to template attributes") } logs.WithField("content", attributes).Debug("Final attributes resolution") info, _ := os.Stat(templaterDir + pathTemplates) if info == nil { logs.WithField("dir", templaterDir+pathTemplates).Debug("Template dir is empty. Nothing to template") return } tmpl, err := template.NewTemplateDir(templaterDir+pathTemplates, target, continueOnError) if err != nil { logs.WithE(err).WithField("dir", templaterDir+pathTemplates).Fatal("Failed to load template dir") } err = tmpl.Process(attributes) if err != nil { logs.WithE(err).WithField("dir", templaterDir+pathTemplates).Fatal("Failed to process template dir") } }
func overrideWithJsonIfNeeded(overrideEnvVarName string, attributes map[string]interface{}) map[string]interface{} { if overrideEnvVarName != "" { if envjson := os.Getenv(overrideEnvVarName); envjson != "" { if len(envjson) > 8 && envjson[0:7] == "base64," { logs.WithField("EnvVar", overrideEnvVarName).Debug("Environment variable is base64 encoded") b64EnvJson := envjson[7:len(envjson)] envjsonBase64Decoded, err := base64.StdEncoding.DecodeString(b64EnvJson) if err != nil { logs.WithE(err).WithField("base64", b64EnvJson).Fatal("Failed to base64 decode") } envjson = string(envjsonBase64Decoded) } logs.WithField("content", envjson).Debug("Override var content") var envattr map[string]interface{} err := json.Unmarshal([]byte(envjson), &envattr) if err != nil { logs.WithE(err). WithField("varName", overrideEnvVarName). WithField("content", envjson). Fatal("Invalid format for environment override content") } attributes = mergemap.Merge(attributes, envattr) } } return attributes }
func main() { rand.Seed(time.Now().UTC().UnixNano()) sigQuitThreadDump() var logLevel string var version bool var oneshot bool rootCmd := &cobra.Command{ Use: "synapse config.yml", PersistentPreRun: func(cmd *cobra.Command, args []string) { if version { fmt.Println("Synapse") fmt.Println("Version :", Version) fmt.Println("Build Time :", BuildTime) os.Exit(0) } if logLevel != "" { level, err := logs.ParseLevel(logLevel) if err != nil { logs.WithField("value", logLevel).Fatal("Unknown log level") } logs.SetLevel(level) } }, Run: func(cmd *cobra.Command, args []string) { if len(args) != 1 { logs.Fatal("Synapse require a configuration file as argument") } synapse, err := LoadConfig(args[0]) if err != nil { logs.WithE(err).Fatal("Cannot start, failed to load configuration") } if err := synapse.Init(Version, BuildTime, logLevel != ""); err != nil { logs.WithE(err).Fatal("Failed to init synapse") } if err := synapse.Start(oneshot); err != nil { logs.WithE(err).Fatal("Failed to start synapse") } waitForSignal() synapse.Stop() }, } rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "L", "", "Set log level") rootCmd.PersistentFlags().BoolVarP(&version, "version", "V", false, "Display version") //rootCmd.PersistentFlags().BoolVarP(&oneshot, "oneshot", "O", false, "run watchers/router only once and exit") if err := rootCmd.Execute(); err != nil { logs.WithE(err).Fatal("Failed to process args") } }
func main() { rand.Seed(time.Now().UTC().UnixNano()) sigQuitThreadDump() var logLevel string var version bool rootCmd := &cobra.Command{ Use: "nerve config.yml", PersistentPreRun: func(cmd *cobra.Command, args []string) { if version { fmt.Println("Nerve") fmt.Println("Version :", Version) fmt.Println("Build Time :", BuildTime) os.Exit(0) } if logLevel != "" { level, err := logs.ParseLevel(logLevel) if err != nil { logs.WithField("value", logLevel).Fatal("Unknown log level") } logs.SetLevel(level) } }, Run: func(cmd *cobra.Command, args []string) { if len(args) != 1 { logs.Fatal("Nerve require a configuration file as argument") } nerve, err := LoadConfig(args[0]) if err != nil { logs.WithE(err).Fatal("Cannot start, failed to load configuration") } if err := nerve.Init(Version, BuildTime, logLevel != ""); err != nil { logs.WithE(err).Fatal("Failed to init nerve") } startStatus := make(chan error) go nerve.Start(startStatus) if status := <-startStatus; status != nil { logs.WithE(status).Fatal("Failed to start nerve") } waitForSignal() nerve.Stop() }, } rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "L", "", "Set log level") rootCmd.PersistentFlags().BoolVarP(&version, "version", "V", false, "Display version") if err := rootCmd.Execute(); err != nil { logs.WithE(err).Fatal("Failed to process args") } }
func importInternalAci(filename string) { content, err := dist.Asset(filename) if err != nil { logs.WithE(err).WithField("aci", filename).Fatal("Cannot found internal aci") } tmpFile := "/tmp/" + RandStringBytesMaskImpr(20) + ".aci" if err := ioutil.WriteFile(tmpFile, content, 0644); err != nil { logs.WithE(err).WithField("aci", filename).Fatal("Failed to write tmp aci to /tmp/tmp.aci") } defer os.Remove(tmpFile) if _, err := Home.Rkt.FetchInsecure(tmpFile); err != nil { logs.WithE(err).Fatal("Failed to import internal image to rkt") } }
func importInternalAci(filename string) { filepath := "dist/bindata/" + filename content, err := dist.Asset(filepath) if err != nil { logs.WithE(err).WithField("aci", filepath).Fatal("Cannot found internal aci") } tmpFile := "/tmp/" + RandStringBytesMaskImpr(20) + ".aci" if err := ioutil.WriteFile(tmpFile, content, 0644); err != nil { logs.WithE(err).WithField("aci", filepath).Fatal("Failed to write tmp aci to /tmp/tmp.aci") } defer os.Remove(tmpFile) if _, err := Home.Rkt.Fetch(tmpFile); err != nil { // TODO does not support concurrency logs.WithE(err).Fatal("Failed to import internal image to rkt") } }
func NewPod(path string, args BuildArgs) (*Pod, error) { fullPath, err := filepath.Abs(path) if err != nil { logs.WithE(err).WithField("path", path).Fatal("Cannot get fullpath") } manifest, err := readPodManifest(fullPath + POD_MANIFEST) if err != nil { return nil, errors.Annotate(err, "Failed to read pod manifest") } fields := data.WithField("pod", manifest.Name.String()) target := path + PATH_TARGET if cnt.Home.Config.TargetWorkDir != "" { currentAbsDir, err := filepath.Abs(cnt.Home.Config.TargetWorkDir + "/" + manifest.Name.ShortName()) if err != nil { logs.WithEF(err, fields).Panic("invalid target path") } target = currentAbsDir } pod := &Pod{ fields: fields, path: fullPath, args: args, target: target, manifest: *manifest, } return pod, nil }
func IpLookupNoError(host string, preferIPv4 bool) net.IP { ip, err := IpLookup(host, preferIPv4) if err != nil { logs.WithE(err).WithField("host", host).Error("Host lookup failed, assume localhost can replace it") ip = net.IPv4(127, 0, 0, 1) } return ip }
func GitHash(path string) (string, error) { out, _, err := ExecCmdGetStdoutAndStderr("git", "-C", path, "rev-parse", "--short", "HEAD") if err != nil { logs.WithE(err).WithField("path", path).Debug("Failed to get git hash from path") return "", err } return out, nil }
func NewAciOrPod(path string, args BuildArgs, checkWg *sync.WaitGroup) DgrCommand { if aci, err := NewAci(path, args, checkWg); err == nil { return aci } else if pod, err2 := NewPod(path, args, checkWg); err2 == nil { return pod } else { logs.WithE(err).WithField("path", path).WithField("err2", err2).Fatal("Cannot construct aci or pod") } return nil }
func (e Env) Generate() { logs.WithFields(e.fields).Debug("Generating units") services := e.ListServices() for _, service := range services { service := e.LoadService(service) if err := service.Generate(); err != nil { logs.WithE(err).Error("Generate failed") } } }
func main() { logs.GetDefaultLog().(*erlog.ErlogLogger).Appenders[0].(*erlog.ErlogWriterAppender).Out = os.Stdout uuid := ProcessArgsAndReturnPodUUID() dir, err := os.Getwd() if err != nil { logs.WithE(err).Fatal("Failed to get current working directory") } b, err := NewBuilder(dir, uuid) if err != nil { logs.WithE(err).Fatal("Failed to load Builder") } if err = b.Build(); err != nil { logs.WithE(err).Fatal("Build failed") } os.Exit(0) }
func Run(overrideEnvVarName string, target string, templaterDir string) { attrMerger, err := merger.NewAttributesMerger(templaterDir + PATH_ATTRIBUTES) if err != nil { logs.WithE(err).Warn("Failed to prepare attributes") } attributes := attrMerger.Merge() attributes = overrideWithJsonIfNeeded(overrideEnvVarName, attributes) err = template.NewTemplateDir(templaterDir+PATH_TEMPLATES, target).Process(attributes) if err != nil { logs.WithE(err).WithField("dir", templaterDir+PATH_TEMPLATES).Fatal("Failed to process template dir") } // out := attrMerger.Merge(fgs.confd_env, res) // err = ioutil.WriteFile("attributes.json", []byte(out), 0777) // if err != nil { // panic(err) // } // read files from dir // run templates }
func overrideWithJsonIfNeeded(overrideEnvVarName string, attributes map[string]interface{}) map[string]interface{} { if overrideEnvVarName != "" { if envjson := os.Getenv(overrideEnvVarName); envjson != "" { logs.WithField("content", envjson).Debug("Override var content") var envattr map[string]interface{} err := json.Unmarshal([]byte(envjson), &envattr) if err != nil { logs.WithE(err). WithField("varName", overrideEnvVarName). WithField("content", envjson). Fatal("Invalid format for environment override content") } attributes = mergemap.Merge(attributes, envattr) } } return attributes }
func newTryCommand(userClean bool) *cobra.Command { cmd := &cobra.Command{ Use: "try", Short: "try templater (experimental)", Long: `try templater (experimental)`, Run: func(cmd *cobra.Command, args []string) { checkNoArgs(args) checkWg := &sync.WaitGroup{} if err := NewAciOrPod(workPath, Args, checkWg).CleanAndTry(); err != nil { logs.WithE(err).Fatal("Try command failed") } checkWg.Wait() }, } return cmd }
func (b *Builder) writeManifest() error { upperId, err := b.upperTreeStoreId() if err != nil { return err } attrMerger, err := merger.NewAttributesMerger(b.stage1Rootfs + PATH_DGR + PATH_BUILDER + PATH_ATTRIBUTES) if err != nil { logs.WithE(err).Warn("Failed to prepare attributes") } attributes := attrMerger.Merge() logs.WithFields(b.fields).WithField("attributes", attributes).Debug("Merged attributes for manifest templating") content, err := ioutil.ReadFile(b.aciTargetPath + common.PathManifestYmlTmpl) if err != nil { return errs.WithEF(err, b.fields.WithField("file", b.aciTargetPath+common.PathManifestYmlTmpl), "Failed to read manifest template") } aciManifest, err := common.ProcessManifestTemplate(string(content), attributes, true) if err != nil { return errs.WithEF(err, b.fields.WithField("content", string(content)), "Failed to process manifest template") } target := b.pod.Root + PATH_OVERLAY + "/" + upperId + PATH_UPPER + common.PathManifest dgrVersion, ok := manifestApp(b.pod).App.Environment.Get(common.EnvDgrVersion) if !ok { return errs.WithF(b.fields, "Cannot find dgr version") } froms, err := aciManifest.GetFroms() if len(froms) != 0 { if froms[0].String() != "" { aciManifest.Aci.Dependencies = append(froms, aciManifest.Aci.Dependencies...) } } if aciManifest.NameAndVersion.Version() == "" { aciManifest.NameAndVersion = *common.NewACFullName(aciManifest.NameAndVersion.Name() + ":" + common.GenerateVersion(b.aciTargetPath)) } if err := common.WriteAciManifest(aciManifest, target, aciManifest.NameAndVersion.Name(), dgrVersion); err != nil { return errs.WithEF(err, b.fields.WithField("file", target), "Failed to write manifest") } return nil }
func newBuildCommand(userClean bool) *cobra.Command { cmd := &cobra.Command{ Use: "build", Short: "build aci or pod", Long: `build an aci or a pod`, Run: func(cmd *cobra.Command, args []string) { checkNoArgs(args) checkWg := &sync.WaitGroup{} if err := NewAciOrPod(workPath, Args, checkWg).CleanAndBuild(); err != nil { logs.WithE(err).Fatal("Build command failed") } checkWg.Wait() }, } cmd.Flags().BoolVarP(&Args.KeepBuilder, "keep-builder", "k", false, "Keep builder container after exit") cmd.Flags().BoolVarP(&Args.CatchOnError, "catch-on-error", "c", false, "Catch a shell on build* runlevel fail") // TODO This is builder dependent and should be pushed by builder ? or find a way to become generic cmd.Flags().BoolVarP(&Args.CatchOnStep, "catch-on-step", "C", false, "Catch a shell after each build* runlevel") return cmd }
func NewPod(path string, args BuildArgs, checkWg *sync.WaitGroup) (*Pod, error) { if (args.CatchOnError || args.CatchOnStep) && !args.SerialBuild { args.SerialBuild = true } fullPath, err := filepath.Abs(path) if err != nil { logs.WithE(err).WithField("path", path).Fatal("Cannot get fullpath") } manifest, err := readPodManifest(fullPath + pathPodManifestYml) if err != nil { manifest2, err2 := readPodManifest(fullPath + "/cnt-pod-manifest.yml") if err2 != nil { return nil, errs.WithEF(err, data.WithField("path", fullPath+pathPodManifestYml).WithField("err2", err2), "Failed to read pod manifest") } logs.WithField("old", "cnt-pod-manifest.yml").WithField("new", "pod-manifest.yml").Warn("You are using the old aci configuration file") manifest = manifest2 } fields := data.WithField("pod", manifest.Name.String()) target := path + pathTarget if Home.Config.TargetWorkDir != "" { currentAbsDir, err := filepath.Abs(Home.Config.TargetWorkDir + "/" + manifest.Name.ShortName()) if err != nil { logs.WithEF(err, fields).Panic("invalid target path") } target = currentAbsDir } pod := &Pod{ checkWg: checkWg, fields: fields, path: fullPath, args: args, target: target, manifest: *manifest, } return pod, nil }
func (e Env) concurrentChecker(services []string) { wg := &sync.WaitGroup{} aChan := make(chan string) for i := 0; i < 3; i++ { wg.Add(1) go func() { for service := range aChan { if err := e.LoadService(service).Check(); err != nil { logs.WithE(err).WithField("service", service).Error("Check failed") } } wg.Done() }() } for _, service := range services { aChan <- service } close(aChan) wg.Wait() }
func newSignCommand(underClean bool) *cobra.Command { cmd := &cobra.Command{ Use: "sign", Short: "sign image", Long: `sign image`, Run: func(cmd *cobra.Command, args []string) { checkNoArgs(args) checkWg := &sync.WaitGroup{} command := NewAciOrPod(workPath, Args, checkWg) if underClean { command.Clean() } if err := command.Sign(); err != nil { logs.WithE(err).Fatal("Sign command failed") } checkWg.Wait() }, } return cmd }
func ProcessArgsAndReturnPodUUID() *types.UUID { flag.Parse() if cliDebugFlag { logs.SetLevel(logs.DEBUG) } if lvlStr := os.Getenv(common.EnvLogLevel); lvlStr != "" { lvl, err := logs.ParseLevel(lvlStr) if err != nil { fmt.Printf("Unknown log level : %s", lvlStr) os.Exit(1) } logs.SetLevel(lvl) } arg := flag.Arg(0) uuid, err := types.NewUUID(arg) if err != nil { logs.WithE(err).WithField("content", arg).Fatal("UUID is missing or malformed") } return uuid }
func newTestCommand(underClean bool) *cobra.Command { cmd := &cobra.Command{ Use: "test", Short: "test image(s)", Long: `test image(s)`, Run: func(cmd *cobra.Command, args []string) { checkNoArgs(args) checkWg := &sync.WaitGroup{} command := NewAciOrPod(workPath, Args, checkWg) if underClean { command.Clean() } if err := command.Test(); err != nil { logs.WithE(err).Fatal("Test command failed") } checkWg.Wait() }, } cmd.Flags().BoolVarP(&Args.NoTestFail, "no-test-fail", "T", false, "Fail if no tests found") cmd.Flags().BoolVarP(&Args.KeepBuilder, "keep-builder", "k", false, "Keep aci & test builder container after exit") return cmd }
func newPushCommand(underClean bool) *cobra.Command { cmd := &cobra.Command{ Use: "push", Short: "push image(s)", Long: `push images to repository`, Run: func(cmd *cobra.Command, args []string) { checkNoArgs(args) checkWg := &sync.WaitGroup{} command := NewAciOrPod(workPath, Args, checkWg) if underClean { command.Clean() } if err := command.Push(); err != nil { logs.WithE(err).Fatal("Push command failed") } checkWg.Wait() }, } cmd.Flags().BoolVarP(&Args.NoTestFail, "no-test-fail", "T", false, "Fail if no tests found") cmd.Flags().BoolVarP(&Args.Test, "test", "t", false, "Run tests before push") return cmd }
func (p *Pod) processAcis() ([]schema.RuntimeApp, error) { apps := make([]schema.RuntimeApp, len(p.manifest.Pod.Apps)) errors := make([]error, len(p.manifest.Pod.Apps)) var wg sync.WaitGroup for i, e := range p.manifest.Pod.Apps { wg.Add(1) f := func(i int, e common.RuntimeApp) { defer wg.Done() app, err := p.processAci(e) if app != nil { apps[i] = *app } errors[i] = err } if p.args.SerialBuild { f(i, e) } else { go f(i, e) } } wg.Wait() failed := 0 for _, err := range errors { if err != nil { logs.WithE(err).Error("Aci process failed") failed++ } } if failed > 0 { return apps, errs.With("Aci process failed") } return apps, nil }
func NewPod(path string, args BuildArgs, checkWg *sync.WaitGroup) (*Pod, error) { if (args.CatchOnError || args.CatchOnStep) && args.ParallelBuild { args.ParallelBuild = false } fullPath, err := filepath.Abs(path) if err != nil { logs.WithE(err).WithField("path", path).Fatal("Cannot get fullpath") } manifest, err := readPodManifest(fullPath + pathPodManifestYml) if err != nil { return nil, errs.WithEF(err, data.WithField("path", fullPath+pathPodManifestYml), "Failed to read pod manifest") } fields := data.WithField("pod", manifest.Name.String()) target := path + pathTarget if Home.Config.TargetWorkDir != "" { currentAbsDir, err := filepath.Abs(Home.Config.TargetWorkDir + "/" + manifest.Name.ShortName()) if err != nil { logs.WithEF(err, fields).Panic("invalid target path") } target = currentAbsDir } pod := &Pod{ checkWg: checkWg, fields: fields, path: fullPath, args: args, target: target, manifest: *manifest, } return pod, nil }
func checkSystemdNspawn() { _, err := utils.ExecCmdGetOutput("systemd-nspawn", "--version") if err != nil { logs.WithE(err).Fatal("system-nspawn is required") } }
logs.WithEF(err, fields).Fatal("Cannot create path directory") } } empty, err := common.IsDirEmpty(workPath) if err != nil { logs.WithEF(err, fields).Fatal("Cannot read path directory") } if !Args.Force { if !empty { logs.WithEF(err, fields).Fatal("Path is not empty cannot init") } } if err := ioutil.WriteFile(workPath+common.PathAciManifest, []byte(initManifestContent), 0644); err != nil { logs.WithEF(err, fields).Fatal("failed to write aci manifest") } defer giveBackUserRights(workPath) checkWg := &sync.WaitGroup{} if err := NewAciOrPod(workPath, Args, checkWg).Init(); err != nil { logs.WithE(err).Fatal("Init command failed") } checkWg.Wait() }, } func init() { initCmd.Flags().BoolVarP(&Args.Force, "force", "f", false, "Force init command if path is not empty") }
checkWg := &sync.WaitGroup{} NewAciOrPod(workPath, Args, checkWg).Clean() checkWg.Wait() }, } var graphCmd = &cobra.Command{ Use: "graph", Short: "generate dependency graph", Long: `generate dependency graph`, Run: func(cmd *cobra.Command, args []string) { checkNoArgs(args) checkWg := &sync.WaitGroup{} if err := NewAciOrPod(workPath, Args, checkWg).Graph(); err != nil { logs.WithE(err).Fatal("Install command failed") } checkWg.Wait() }, } var aciVersion = &cobra.Command{ Use: "aci-version file", Short: "display version of aci", Run: func(cmd *cobra.Command, args []string) { if len(args) != 1 { cmd.Usage() os.Exit(1) } im, err := common.ExtractManifestFromAci(args[0]) if err != nil {
func (f *TemplateFile) runTemplate(dst string, attributes map[string]interface{}, failOnNoValue bool) error { if logs.IsTraceEnabled() { logs.WithF(f.fields).WithField("attributes", attributes).WithField("failOnNoValue", failOnNoValue).Trace("templating with attributes") } fields := f.fields.WithField("dst", dst) logs.WithF(fields).Info("Templating file") out, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, f.Mode) if err != nil { return errs.WithEF(err, fields, "Cannot open destination file") } defer func() { out.Close() }() buff := bytes.Buffer{} writer := bufio.NewWriter(&buff) if err := f.template.Execute(writer, attributes); err != nil { return errs.WithEF(err, fields, "Templating execution failed") } if err := writer.Flush(); err != nil { return errs.WithEF(err, fields, "Failed to flush buffer") } buff.WriteByte('\n') b := buff.Bytes() if logs.IsTraceEnabled() { logs.WithF(f.fields).WithField("result", string(b)).Trace("templating done") } scanner := bufio.NewScanner(bytes.NewReader(b)) // TODO this sux scanner.Split(bufio.ScanLines) for i := 1; scanner.Scan(); i++ { text := scanner.Text() if bytes.Contains([]byte(text), []byte("<no value>")) { err = errs.WithF(fields.WithField("line", i).WithField("text", text), "Templating result have <no value>") if failOnNoValue { return err } else { logs.WithE(err).Error("Templating result have <no value>") } } } if length, err := out.Write(b); length != len(b) || err != nil { return errs.WithEF(err, fields, "Write to file failed") } if err = out.Sync(); err != nil { return errs.WithEF(err, fields, "Failed to sync output file") } if err = os.Chmod(dst, f.Mode); err != nil { return errs.WithEF(err, fields.WithField("file", dst), "Failed to set mode on file") } if err = os.Chown(dst, f.Uid, f.Gid); err != nil { return errs.WithEF(err, fields.WithField("file", dst), "Failed to set owner of file") } if f.CheckCmd != "" { cmd := exec.Command("/dgr/bin/busybox", "sh", "-c", f.CheckCmd) cmd.Stdout = os.Stdout cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr if err = cmd.Run(); err != nil { return errs.WithEF(err, fields.WithField("file", dst), "Check command failed after templating") } } return err }
func prepareServiceCommands(service *work.Service) *cobra.Command { var ttl string serviceCmd := &cobra.Command{ Use: service.Name, Short: "run command for " + service.Name + " on env " + service.GetEnv().GetName(), } generateCmd := &cobra.Command{ Use: "generate", Short: "generate units for " + service.Name + " on env " + service.GetEnv().GetName(), Long: `generate units using remote resolved or local pod/aci manifests`, Run: func(cmd *cobra.Command, args []string) { if err := service.Generate(); err != nil { logs.WithE(err).Fatal("Generate failed") } }, } checkCmd := &cobra.Command{ Use: "check [manifest...]", Short: "Check units for " + service.Name + " on env " + service.GetEnv().GetName(), Run: func(cmd *cobra.Command, args []string) { if err := service.Check(); err != nil { logs.WithE(err).Fatal("Check failed") } }, } diffCmd := &cobra.Command{ Use: "diff [manifest...]", Short: "diff units for " + service.Name + " on env " + service.GetEnv().GetName(), Run: func(cmd *cobra.Command, args []string) { service.Diff() }, } lockCmd := &cobra.Command{ Use: "lock [message...]", Short: "lock " + service.Name + " on env " + service.GetEnv().GetName(), Long: `Add a lock to the service in etcd to prevent somebody else to do modification actions on this service/units.` + `lock is ignored if set by the current user`, Run: func(cmd *cobra.Command, args []string) { if len(args) == 0 { logs.Fatal("Please add a message to describe lock") } message := strings.Join(args, " ") ttl, err := time.ParseDuration(ttl) if err != nil { logs.WithError(err).Fatal("Wrong value for ttl") } service.Lock("service/lock", ttl, message) }, } unlockCmd := &cobra.Command{ Use: "unlock", Short: "unlock " + service.Name + " on env " + service.GetEnv().GetName(), Run: func(cmd *cobra.Command, args []string) { service.Unlock("service/unlock") }, } listCmd := &cobra.Command{ Use: "list-units", Short: "list-units on " + service.Name + " on env " + service.GetEnv().GetName(), Run: func(cmd *cobra.Command, args []string) { service.FleetListUnits("service/unlock") }, } updateCmd := &cobra.Command{ Use: "update", Short: "update " + service.Name + " on env " + service.GetEnv().GetName(), Run: func(cmd *cobra.Command, args []string) { err := service.Update() if err != nil { os.Exit(1) } }, } lockCmd.Flags().StringVarP(&ttl, "duration", "t", "1h", "lock duration") updateCmd.Flags().BoolVarP(&work.BuildFlags.All, "all", "a", false, "process all units, even up to date") updateCmd.Flags().BoolVarP(&work.BuildFlags.Yes, "yes", "y", false, "process units without asking") serviceCmd.AddCommand(generateCmd, lockCmd, unlockCmd, updateCmd, checkCmd, diffCmd, listCmd) // var units []string // hystrix.Go("list_units", func() error { // units = service.ListUnits() // return nil // }, func(err error) error { // entry := service.GetLog() // entry.WithError(err).Warn("Cannot list units. Some command may be missing") // return nil // }) for _, unitName := range service.ListUnits() { unit := service.LoadUnit(unitName) serviceCmd.AddCommand(prepareUnitCommands(unit)) } return serviceCmd }