// Deploy allows the creation of deploy.Deployments remotely func (s *SpreadCli) Deploy() *cli.Command { return &cli.Command{ Name: "deploy", Usage: "spread deploy [-s] PATH | COMMIT [kubectl context]", Description: "Deploys objects to a remote Kubernetes cluster.", ArgsUsage: "-s will deploy only if no other deployment found (otherwise fails)", Action: func(c *cli.Context) { ref := c.Args().First() var dep *deploy.Deployment proj, err := s.project() if err == nil { var docs map[string]*pb.Document if len(ref) == 0 { s.printf("Deploying from index...") docs, err = proj.Index() if err != nil { s.fatalf("Error getting index: %v", err) } if err = s.promptForArgs(docs, false); err == nil { dep, err = deploy.DeploymentFromDocMap(docs) } } else { if docs, err = proj.ResolveCommit(ref); err == nil { if err = s.promptForArgs(docs, false); err == nil { dep, err = deploy.DeploymentFromDocMap(docs) } } else { dep, err = s.globalDeploy(ref) } } } else { dep, err = s.globalDeploy(ref) } if err != nil { s.fatalf("Failed to assemble deployment: %v", err) } context := c.Args().Get(1) cluster, err := deploy.NewKubeClusterFromContext(context) if err != nil { s.fatalf("Failed to deploy: %v", err) } s.printf("Deploying %d objects using the %s.", dep.Len(), displayContext(context)) update := !c.Bool("s") err = cluster.Deploy(dep, update, false) if err != nil { //TODO: make better error messages (one to indicate a deployment already existed; another one if a deployment did not exist but some other error was thrown s.fatalf("Did not deploy.: %v", err) } s.printf("Deployment successful!") }, } }
// Status returns information about the current state of the project. func (s SpreadCli) Status() *cli.Command { return &cli.Command{ Name: "status", Usage: "spread status", Description: "Information about what's commited, changed, and staged.", Action: func(c *cli.Context) { proj := s.projectOrDie() indexDocs, err := proj.Index() if err != nil { s.fatalf("Could not load Index: %v", err) } index, err := deploy.DeploymentFromDocMap(indexDocs) if err != nil { s.fatalf("Failed to create KubeObject from index doc: %v", err) } var head *deploy.Deployment headDocs, err := proj.Head() if err == nil { head, err = deploy.DeploymentFromDocMap(headDocs) if err != nil { s.fatalf("Failed to create KubeObject from HEAD doc: %v", err) } } else { head = new(deploy.Deployment) } client, err := deploy.NewKubeClusterFromContext("") if err != nil { s.fatalf("Failed to connect to Kubernetes cluster: %v", err) } cluster, err := client.Deployment() if err != nil { s.fatalf("Could not load deployment from cluster: %v", err) } stat := deploy.Stat(index, head, cluster) s.printStatus(stat) }, } }
// Diff shows the difference bettwen the cluster and the index. func (s SpreadCli) Diff() *cli.Command { return &cli.Command{ Name: "diff", Usage: "spread diff", Description: "Diffs index against state of cluster", Flags: []cli.Flag{ cli.StringFlag{ Name: "context", Value: "", Usage: "kubectl context to use for requests", }, }, Action: func(c *cli.Context) { proj := s.projectOrDie() docs, err := proj.Index() if err != nil { s.fatalf("Could not load Index: %v", err) } index, err := deploy.DeploymentFromDocMap(docs) if err != nil { s.fatalf("Failed to create Deployment from Documents: %v", err) } context := c.String("context") client, err := deploy.NewKubeClusterFromContext(context) if err != nil { s.fatalf("Failed to connect to Kubernetes cluster: %v", err) } cluster, err := client.Deployment() if err != nil { s.fatalf("Could not load deployment from cluster: %v", err) } s.printf(index.Diff(cluster)) }, } }
// Add sets up a Spread repository for versioning. func (s SpreadCli) Add() *cli.Command { return &cli.Command{ Name: "add", Usage: "spread add <path>", Description: "Stage objects to the index", Flags: []cli.Flag{ cli.StringFlag{ Name: "namespace", Value: "default", Usage: "namespace to look for objects", }, cli.StringFlag{ Name: "context", Value: "", Usage: "kubectl context to use for requests", }, cli.BoolFlag{ Name: "no-export", Usage: "don't request Kube API server to export objects", }, cli.BoolFlag{ Name: "clean", Usage: "Removes fields that are known to cause issues with reproducibility", }, }, Action: func(c *cli.Context) { // Download specified object from Kubernetes cluster // example: spread add rc/mattermost resource := c.Args().First() if len(resource) == 0 { s.fatalf("A resource to be added must be specified") } context := c.String("context") cluster, err := deploy.NewKubeClusterFromContext(context) if err != nil { s.fatalf("Failed to connect to Kubernetes cluster: %v", err) } // parse resource type and name parts := strings.Split(resource, "/") if len(parts) != 2 { s.fatalf("Unrecognized resource format") } kind, name := parts[0], parts[1] namespace := c.String("namespace") export := !c.Bool("no-export") kubeObj, err := cluster.Get(kind, namespace, name, export) if err != nil { s.fatalf("Could not get object from cluster: %v", err) } if c.Bool("clean") { err = cleanObj(kubeObj) if err != nil { s.fatalf("Could not get object from cluster: %v", err) } } // TODO(DG): Clean this up gvk := kubeObj.GetObjectKind().GroupVersionKind() gvk.Version = "v1" kubeObj.GetObjectKind().SetGroupVersionKind(gvk) kubeObj.GetObjectMeta().SetNamespace(namespace) path, err := deploy.ObjectPath(kubeObj) if err != nil { s.fatalf("Failed to determine path to save object: %v", err) } obj, err := data.CreateDocument(kubeObj.GetObjectMeta().GetName(), path, kubeObj) if err != nil { s.fatalf("failed to encode document: %v", err) } proj := s.projectOrDie() err = proj.AddDocumentToIndex(obj) if err != nil { s.fatalf("Failed to add object to Git index: %v", err) } }, } }