diff --git a/bin/p2-inspect/README.md b/bin/p2-inspect/README.md deleted file mode 100644 index 197d1b252..000000000 --- a/bin/p2-inspect/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# p2-inspect - -`p2-inspect` is a tool for examining the state of a p2 cluster. Given the address of any consul agent, it will pull the manifest SHAs and health checks for all the pods running in the cluster, across all its nodes. Example: - -```bash -$ p2-inspect | python -mjson.tool -``` - -```json -{ - "isup": { - "aws1.example.com": { - "health_check": { - "output": "[/data/pods/isup/isup/installs/isup_vsjlzlxvnkizuxmkutqmkqhwukyryztuxhusnkpm/bin/launch]\n[PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin TERM=linux RUNLEVEL=3 PREVLEVEL=N UPSTART_EVENTS=runlevel UPSTART_JOB=runit UPSTART_INSTANCE= CONFIG_PATH=/data/pods/isup/config/isup_717cc0d58df240e2668c865cdc063d715446e6db09ad4b64f7a2f0f4e361ea8f.yaml]\n", - "status": "passing" - }, - "intent_manifest_sha": "717cc0d58df240e2668c865cdc063d715446e6db09ad4b64f7a2f0f4e361ea8f", - "reality_manifest_sha": "717cc0d58df240e2668c865cdc063d715446e6db09ad4b64f7a2f0f4e361ea8f" - }, - "aws2.example.com": { - "health_check": { - "output": "[/data/pods/isup/isup/installs/isup_tkkhnurngovsomvzikznymgmluohzjvniwzrtpxq/bin/launch]\n[PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin TERM=linux RUNLEVEL=3 PREVLEVEL=N UPSTART_EVENTS=runlevel UPSTART_JOB=runit UPSTART_INSTANCE= CONFIG_PATH=/data/pods/isup/config/isup_b56d3c3fd3c264841c8aad6a9ce6f06271a62dc6daffeef0efb6b50d86424bc6.yaml]\n", - "status": "passing" - }, - "intent_manifest_sha": "b56d3c3fd3c264841c8aad6a9ce6f06271a62dc6daffeef0efb6b50d86424bc6", - "reality_manifest_sha": "b56d3c3fd3c264841c8aad6a9ce6f06271a62dc6daffeef0efb6b50d86424bc6" - } - }, - "p2-preparer": { - "aws1.example.com": { - "intent_manifest_sha": "9b9c7cb38b9a68564d6d582c05298cd3dab02e9d22c8178e407eaaee0726169e", - "reality_manifest_sha": "9b9c7cb38b9a68564d6d582c05298cd3dab02e9d22c8178e407eaaee0726169e" - }, - "aws2.example.com": { - "intent_manifest_sha": "9b9c7cb38b9a68564d6d582c05298cd3dab02e9d22c8178e407eaaee0726169e", - "reality_manifest_sha": "9b9c7cb38b9a68564d6d582c05298cd3dab02e9d22c8178e407eaaee0726169e" - }, - "aws3.example.com": { - "intent_manifest_sha": "9b9c7cb38b9a68564d6d582c05298cd3dab02e9d22c8178e407eaaee0726169e", - "reality_manifest_sha": "9b9c7cb38b9a68564d6d582c05298cd3dab02e9d22c8178e407eaaee0726169e" - } - } -} -``` - -This indicates that there are three nodes running the `p2-preparer` pod. This pod has no health check; hence there is no `health_check` object in the JSON. Meanwhile, there are also two instances of the `isup` pod. These pods both have passing health checks, and their health check scripts produced some output that you can see in the JSON above. - -You can filter by pod ID, by node name, or by both: - -```bash -$ p2-inspect --node aws1.example.com --pod isup | python -m json.tool -``` - -```json -{ - "isup": { - "aws1.example.com": { - "health_check": { - "output": "[/data/pods/isup/isup/installs/isup_vsjlzlxvnkizuxmkutqmkqhwukyryztuxhusnkpm/bin/launch]\n[PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin TERM=linux RUNLEVEL=3 PREVLEVEL=N UPSTART_EVENTS=runlevel UPSTART_JOB=runit UPSTART_INSTANCE= CONFIG_PATH=/data/pods/isup/config/isup_717cc0d58df240e2668c865cdc063d715446e6db09ad4b64f7a2f0f4e361ea8f.yaml]\n", - "status": "passing" - }, - "intent_manifest_sha": "717cc0d58df240e2668c865cdc063d715446e6db09ad4b64f7a2f0f4e361ea8f", - "reality_manifest_sha": "717cc0d58df240e2668c865cdc063d715446e6db09ad4b64f7a2f0f4e361ea8f" - } - } -} -``` diff --git a/bin/p2-inspect/main.go b/bin/p2-inspect/main.go deleted file mode 100644 index e2fc97776..000000000 --- a/bin/p2-inspect/main.go +++ /dev/null @@ -1,124 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "os" - - "gopkg.in/alecthomas/kingpin.v2" - - "github.com/square/p2/pkg/health/checker" - "github.com/square/p2/pkg/inspect" - "github.com/square/p2/pkg/store/consul" - "github.com/square/p2/pkg/store/consul/consulutil" - "github.com/square/p2/pkg/store/consul/flags" - "github.com/square/p2/pkg/types" - "github.com/square/p2/pkg/version" -) - -var ( - nodeArg = kingpin.Flag("node", "The node to inspect. By default, all nodes are shown.").String() - podArg = kingpin.Flag("pod", "The pod manifest ID to inspect. By default, all pods are shown.").String() - format = kingpin.Flag("format", "Display format").Default("tree").Enum("tree", "list") -) - -func main() { - kingpin.Version(version.VERSION) - _, opts, _ := flags.ParseWithConsulOptions() - client := consul.NewConsulClient(opts) - store := consul.NewConsulStore(client) - - var intents []consul.ManifestResult - var realities []consul.ManifestResult - var err error - filterNodeName := types.NodeName(*nodeArg) - filterPodID := types.PodID(*podArg) - - if filterNodeName != "" { - intents, _, err = store.ListPods(consul.INTENT_TREE, filterNodeName) - } else { - intents, _, err = store.AllPods(consul.INTENT_TREE) - } - if err != nil { - message := "Could not list intent kvpairs: %s" - if kvErr, ok := err.(consulutil.KVError); ok { - log.Fatalf(message, kvErr.KVError) - } else { - log.Fatalf(message, err) - } - } - - if filterNodeName != "" { - realities, _, err = store.ListPods(consul.REALITY_TREE, filterNodeName) - } else { - realities, _, err = store.AllPods(consul.REALITY_TREE) - } - - if err != nil { - message := "Could not list reality kvpairs: %s" - if kvErr, ok := err.(consulutil.KVError); ok { - log.Fatalf(message, kvErr.KVError) - } else { - log.Fatalf(message, err) - } - } - - statusMap := make(map[types.PodID]map[types.NodeName]inspect.NodePodStatus) - - for _, kvp := range intents { - if err = inspect.AddKVPToMap(kvp, inspect.INTENT_SOURCE, filterNodeName, filterPodID, statusMap); err != nil { - log.Fatal(err) - } - } - - for _, kvp := range realities { - if err = inspect.AddKVPToMap(kvp, inspect.REALITY_SOURCE, filterNodeName, filterPodID, statusMap); err != nil { - log.Fatal(err) - } - } - - hchecker := checker.NewHealthChecker(client) - for podID := range statusMap { - resultMap, err := hchecker.Service(podID.String()) - if err != nil { - log.Fatalf("Could not retrieve health checks for pod %s: %s", podID, err) - } - - for node, result := range resultMap { - if filterNodeName != "" && node != filterNodeName { - continue - } - - old := statusMap[podID][node] - old.Health = result.Status - statusMap[podID][node] = old - } - } - - // Keep this switch in sync with the enum options for the "format" flag. Rethink this - // design once there are many different formats. - switch *format { - case "tree": - // Native data format is already a "tree" - enc := json.NewEncoder(os.Stdout) - err = enc.Encode(statusMap) - case "list": - // "List" format is a flattened version of "tree" - var output []inspect.NodePodStatus - for podID, nodes := range statusMap { - for node, status := range nodes { - status.PodId = podID - status.NodeName = node - output = append(output, status) - } - } - enc := json.NewEncoder(os.Stdout) - err = enc.Encode(output) - default: - err = fmt.Errorf("unrecognized format: %s", *format) - } - if err != nil { - log.Fatal(err) - } -} diff --git a/bin/p2-label/main.go b/bin/p2-label/main.go deleted file mode 100644 index 9b93b02bc..000000000 --- a/bin/p2-label/main.go +++ /dev/null @@ -1,178 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strings" - - "github.com/square/p2/pkg/labels" - "github.com/square/p2/pkg/store/consul/flags" - "gopkg.in/alecthomas/kingpin.v2" - klabels "k8s.io/kubernetes/pkg/labels" -) - -var ( - cmdApply = kingpin.Command(CmdApply, "Apply label changes to all objects matching a selector") - applyAutoConfirm = cmdApply.Flag("yes", "Autoconfirm label applications. Use with caution!").Short('y').Bool() - applyLabelType = cmdApply.Flag("labelType", "The type of label to adjust. Sometimes called the \"label tree\". Supported types can be found here:\n\thttps://godoc.org/github.com/square/p2/pkg/labels#pkg-constants").Short('t').Required().String() - applySubjectSelector = cmdApply.Flag("selector", "The selector on which to modify labels. Exclusive with ID").Short('s').String() - applySubjectID = cmdApply.Flag("id", "The id of the entry to apply labels to. Exclusive with selector").String() - applyAddititiveLabels = cmdApply.Flag("add", `The label set to apply to the subject. Include multiple --add switches to include multiple labels. It's safe to mix --add with --delete though the results of this command are not transactional. - -Example: - p2-label --selector $selector --add foo=bar --add bar=baz -`).Short('a').StringMap() - applyDestructiveLabels = cmdApply.Flag("delete", `The list of label keys to remove from the nodes in the selector. Deletes are idempotent. Include multiple --delete switches to include multiple labels. It's safe to mix --add with --delete though the results of this command are not transactional. - -Example: - p2-label --selector $selector --delete foo --delete bar -`).Short('d').Strings() - - cmdShow = kingpin.Command(CmdShow, "Show labels that apply to a particular entity (type, ID)") - showLabelType = cmdShow.Flag("labelType", "The type of label to adjust. Sometimes called the \"label tree\". Supported types can be found here:\n\thttps://godoc.org/github.com/square/p2/pkg/labels#pkg-constants").Short('t').Required().String() - showID = cmdShow.Flag("id", "The ID of the entity to show labels for.").Short('i').Required().String() - - // autoConfirm captures the confirmation desire abstractly across commands - autoConfirm = false -) - -const ( - CmdApply = "apply" - CmdShow = "show" -) - -func main() { - cmd, _, applicator := flags.ParseWithConsulOptions() - exitCode := 0 - - switch cmd { - case CmdShow: - labelType, err := labels.AsType(*showLabelType) - if err != nil { - fmt.Fprintf(os.Stderr, "Error while parsing label type. Check the commandline.\n%v\n", err) - exitCode = 1 - break - } - - labelsForEntity, err := applicator.GetLabels(labelType, *showID) - if err != nil { - fmt.Fprintf(os.Stderr, "Got error while querying labels. %v\n", err) - exitCode = 1 - break - } - fmt.Printf("%s/%s: %s\n", labelType, *showID, labelsForEntity.Labels.String()) - return - case CmdApply: - // if xnor(selector, id) - if (*applySubjectSelector == "") == (*applySubjectID == "") { - fmt.Fprint(os.Stderr, "Must pass either an ID or a selector for objects to apply the given label to") - exitCode = 1 - break - } - autoConfirm = *applyAutoConfirm - - labelType, err := labels.AsType(*applyLabelType) - if err != nil { - fmt.Fprintf(os.Stderr, "Unrecognized type %s. Check the commandline and documentation.\nhttps://godoc.org/github.com/square/p2/pkg/labels#pkg-constants\n", *applyLabelType) - exitCode = 1 - break - } - - additiveLabels := *applyAddititiveLabels - destructiveKeys := *applyDestructiveLabels - - var matches []labels.Labeled - if *applySubjectSelector != "" { - subject, err := klabels.Parse(*applySubjectSelector) - if err != nil { - fmt.Fprintf(os.Stderr, "Error while parsing subject label. Check the syntax.\n%v\n", err) - exitCode = 1 - break - } - - matches, err = applicator.GetMatches(subject, labelType) - if err != nil { - if labels.IsNoLabelsFound(err) { - fmt.Fprintf(os.Stderr, "No labels were found for the %s type", labelType) - } else { - fmt.Fprintf(os.Stderr, "Error while finding label matches. Check the syntax.\n%v\n", err) - } - - exitCode = 1 - break - } - } else { - matches = []labels.Labeled{{ID: *applySubjectID}} - } - - if len(additiveLabels) > 0 { - fmt.Printf("labels to be added: %s\n", klabels.Set(additiveLabels)) - } - - if len(destructiveKeys) > 0 { - fmt.Printf("labels to be removed: %s\n", destructiveKeys) - } - - var labelsForEntity labels.Labeled - for _, match := range matches { - entityID := match.ID - - err := applyLabels(applicator, entityID, labelType, additiveLabels, destructiveKeys) - if err != nil { - fmt.Printf("Encountered err during labeling, %v", err) - exitCode = 1 - } - - labelsForEntity, err = applicator.GetLabels(labelType, entityID) - if err != nil { - fmt.Fprintf(os.Stderr, "Got error while querying labels. %v\n", err) - exitCode = 1 - continue - } - fmt.Printf("%s/%s: %s\n", labelType, entityID, labelsForEntity.Labels.String()) - } - break - } - - os.Exit(exitCode) -} - -func applyLabels(applicator labels.ApplicatorWithoutWatches, entityID string, labelType labels.Type, additiveLabels map[string]string, destructiveKeys []string) error { - var err error - if !confirm(fmt.Sprintf("mutate the labels for %s/%s", labelType, entityID)) { - return nil - } - if len(additiveLabels) > 0 { - for k, v := range additiveLabels { - err = applicator.SetLabel(labelType, entityID, k, v) - if err != nil { - fmt.Fprintf(os.Stderr, "Error while appyling label. k/v: %s/%s.\n%v\n", k, v, err) - } - } - } - if len(destructiveKeys) > 0 { - for _, key := range destructiveKeys { - err = applicator.RemoveLabel(labelType, entityID, key) - if err != nil { - fmt.Fprintf(os.Stderr, "Error while destroying label with key: %s.\n%v\n", key, err) - } - } - } - return nil -} - -func confirm(message string) bool { - if autoConfirm { - return true - } - - fmt.Printf("Confirm your intention to %s\n", message) - fmt.Printf(`Type "y" to confirm [n]: `) - var input string - _, err := fmt.Scanln(&input) - if err != nil { - return false - } - resp := strings.TrimSpace(strings.ToLower(input)) - return resp == "y" || resp == "yes" -} diff --git a/bin/p2-log-bridge/main.go b/bin/p2-log-bridge/main.go deleted file mode 100644 index 13e90b675..000000000 --- a/bin/p2-log-bridge/main.go +++ /dev/null @@ -1,110 +0,0 @@ -package main - -import ( - "fmt" - "io" - "os" - "os/exec" - "sync" - - "github.com/square/p2/pkg/logbridge" - "github.com/square/p2/pkg/logging" - "github.com/square/p2/pkg/version" - "golang.org/x/sys/unix" - "gopkg.in/alecthomas/kingpin.v2" -) - -var ( - durableLogger = kingpin.Arg("exec", "An executable that logbridge will log to without dropping messages. If a write to STDIN of this program blocks, logbridge will block.").Required().String() -) - -func main() { - kingpin.Version(version.VERSION) - kingpin.Parse() - - if err := setSTDINToBlock(); err != nil { - logging.DefaultLogger.WithError(err).Error("fatal error setting STDIN to block") - os.Exit(1) - } - - if err := discardSTDOUTAndSTDERR(); err != nil { - logging.DefaultLogger.WithError(err).Error("fatal error discarding STDOUT and STDERR") - os.Exit(1) - } - - var wg sync.WaitGroup - loggerCmd := exec.Command(*durableLogger) - durablePipe, err := loggerCmd.StdinPipe() - if err != nil { - logging.DefaultLogger.WithError(err).Error("fatal error during configuration of subordinate log command") - } - - wg.Add(1) - go func(loggerCmd exec.Cmd) { - defer wg.Done() - - if err := loggerCmd.Start(); err != nil { - logging.DefaultLogger.WithError(err).Errorln("fatal error during execution of subordinate log command") - os.Exit(1) - } - - err := loggerCmd.Wait() - if err != nil { - logging.DefaultLogger.WithError(err).Errorln("fatal error in subordinate log command") - os.Exit(1) - } - }(*loggerCmd) - - wg.Add(1) - go func(r io.Reader, durableWriter, lossyWriter io.Writer, logger logging.Logger) { - defer wg.Done() - - lb := logbridge.NewLogBridge(r, durableWriter, lossyWriter, logger, 1024, 4096, nil, "log_lines", "log_bytes", "dropped_lines", "throttled_ms") - - lb.Tee() - logging.DefaultLogger.NoFields().Infoln("logbridge Tee returned. Shutting down subordinate log command.") - durablePipe.Close() - }(os.Stdin, durablePipe, os.Stdout, logging.DefaultLogger) - - logging.DefaultLogger.NoFields().Info("logging running in background…") - wg.Wait() -} - -// In environments where svlogd is used, the pipe that becomes STDIN of this -// program can be non-blocking. Go's File implementation does not play well -// with non-blocking pipes, in particular it does not recover from an EAGAIN -// error from read(2). -// This function defensively sets its 0th file descriptor to be blocking so -// that we do not have to handle EAGAIN errors. -func setSTDINToBlock() error { - oldflags, _, errno := unix.Syscall(unix.SYS_FCNTL, 0, unix.F_GETFL, 0) - if errno != 0 { - return fmt.Errorf("unix.FCNTL F_GETFL errno: %d", errno) - } - _, _, errno = unix.Syscall(unix.SYS_FCNTL, 0, unix.F_SETFL, oldflags&^unix.O_NONBLOCK) - if errno != 0 { - return fmt.Errorf("unix.FCNTL F_SETFL errno: %d", errno) - } - return nil -} - -// Environments that use systemd-journald may be affected by a bug in which -// logbridge write()s will fail with EPIPE if systemd-journald restarts. To -// avoid this, we can update the STDOUT and STDERR file descriptors to point at -// /dev/null. -func discardSTDOUTAndSTDERR() error { - dn, err := os.Open(os.DevNull) - if err != nil { - return fmt.Errorf("could not open %s: %s", os.DevNull, err) - } - dnFd := int(dn.Fd()) - outFd := int(os.Stdout.Fd()) - if err := unix.Dup2(dnFd, outFd); err != nil { - return fmt.Errorf("could not copy %s to file descriptor %d: %s", os.DevNull, outFd, err) - } - errFd := int(os.Stderr.Fd()) - if err := unix.Dup2(dnFd, errFd); err != nil { - return fmt.Errorf("could not copy %s to file descriptor %d: %s", os.DevNull, errFd, err) - } - return nil -} diff --git a/bin/p2-pcctl/main.go b/bin/p2-pcctl/main.go deleted file mode 100644 index aafa9f8a5..000000000 --- a/bin/p2-pcctl/main.go +++ /dev/null @@ -1,432 +0,0 @@ -package main - -import ( - "encoding/json" - "errors" - "fmt" - "log" - "os" - "os/user" - "time" - - "gopkg.in/alecthomas/kingpin.v2" - - "github.com/sirupsen/logrus" - "github.com/square/p2/pkg/cli" - "github.com/square/p2/pkg/labels" - "github.com/square/p2/pkg/logging" - "github.com/square/p2/pkg/pc/control" - "github.com/square/p2/pkg/pc/fields" - rc_fields "github.com/square/p2/pkg/rc/fields" - "github.com/square/p2/pkg/store/consul" - "github.com/square/p2/pkg/store/consul/flags" - "github.com/square/p2/pkg/store/consul/pcstore" - "github.com/square/p2/pkg/types" - klabels "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/util/sets" -) - -const ( - cmdCreateText = "create" - cmdGetText = "get" - cmdDeleteText = "delete" - cmdUpdateAnnotationsText = "update-annotations" - cmdUpdateSelectorText = "update-selector" - cmdUpdateStrategyText = "update-strategy" - cmdListText = "list" -) - -// "create" command and flags -var ( - cmdCreate = kingpin.Command(cmdCreateText, "Create a pod cluster. ") - createPodID = cmdCreate.Flag("pod", "The pod ID on the pod cluster").Required().String() - createAZ = cmdCreate.Flag("az", "The availability zone of the pod cluster").Required().String() - createName = cmdCreate.Flag("name", "The cluster name (ie. staging, production)").Required().String() - createAnnotations = cmdCreate.Flag("annotations", "Complete set of annotations - must parse as JSON!").String() - createStrategy = cmdCreate.Flag("allocation-strategy", "The allocation strategy to use for RCs created for this pod cluster").Required().Enum(rc_fields.StaticStrategy.String(), rc_fields.DynamicStrategy.String()) -) - -// "get" command and flags -var ( - cmdGet = kingpin.Command(cmdGetText, "Show a pod cluster. ") - getPodID = cmdGet.Flag("pod", "The pod ID on the pod cluster").String() - getAZ = cmdGet.Flag("az", "The availability zone of the pod cluster").String() - getName = cmdGet.Flag("name", "The cluster name (ie. staging, production)").String() - getID = cmdGet.Flag("id", "The cluster UUID. This option is mutually exclusive with pod,az,name").String() -) - -// "delete" command and flags -var ( - cmdDelete = kingpin.Command(cmdDeleteText, "Delete a pod cluster. ") - deletePodID = cmdDelete.Flag("pod", "The pod ID on the pod cluster").String() - deleteAZ = cmdDelete.Flag("az", "The availability zone of the pod cluster").String() - deleteName = cmdDelete.Flag("name", "The cluster name (ie. staging, production)").String() - deleteID = cmdDelete.Flag("id", "The cluster UUID. This option is mutually exclusive with pod,az,name").String() -) - -// "update-annotations" command and flags" -var ( - cmdUpdateAnnotations = kingpin.Command(cmdUpdateAnnotationsText, "Update a pod cluster's annotations.") - - // these flags identify the pod cluster to update - updateAnnotationsPodID = cmdUpdateAnnotations.Flag("pod", "The pod ID on the pod cluster that should be updated.").String() - updateAnnotationsAZ = cmdUpdateAnnotations.Flag("az", "The availability zone of the pod cluster that should be updated").String() - updateAnnotationsName = cmdUpdateAnnotations.Flag("name", "The cluster name (ie. staging, production) for the pod cluster that should be updated.").String() - updateAnnotationsID = cmdUpdateAnnotations.Flag("id", "The UUID of the pod cluster that should be updated. This option is mutually exclusive with pod,az,name").String() - - // this flag specifies the annotations to update. - updateAnnotations = cmdUpdateAnnotations.Flag("annotations", "JSON string representing the complete annotations that should be applied to the pod cluster. Annotations will not be updated if this flag is unspecified.").Required().String() -) - -// "update-selector" command and flags -var ( - cmdUpdateSelector = kingpin.Command(cmdUpdateSelectorText, "Update a pod cluster's pod selector. A diff of pod cluster membership and a confirmation prompt will be shown before submitting the update") - - // these flags identify the pod cluster to update - updateSelectorPodID = cmdUpdateSelector.Flag("pod", "The pod ID on the pod cluster that should be updated.").String() - updateSelectorAZ = cmdUpdateSelector.Flag("az", "The availability zone of the pod cluster that should be updated").String() - updateSelectorName = cmdUpdateSelector.Flag("name", "The cluster name (ie. staging, production) for the pod cluster that should be updated.").String() - updateSelectorID = cmdUpdateSelector.Flag("id", "The UUID of the pod cluster that should be updated. This option is mutually exclusive with pod,az,name").String() - - // this flag specifies the selector to update the pod cluster with - updateSelector = cmdUpdateSelector.Flag("selector", "The label selector to use for the pod cluster's pod selector (e.g. \"pod_id=foo,availability_zone=bar\")").Required().String() -) - -// "update-strategy" command -var ( - cmdUpdateStrategy = kingpin.Command(cmdUpdateStrategyText, "Update a pod cluster's allocation strategy. This determines whether RCs created for this pod cluster will automatically change node membership when hosts are down") - - // these flags identify the pod cluster to update - updateStrategyPodID = cmdUpdateStrategy.Flag("pod", "The pod ID on the pod cluster that should be updated.").String() - updateStrategyAZ = cmdUpdateStrategy.Flag("az", "The availability zone of the pod cluster that should be updated").String() - updateStrategyName = cmdUpdateStrategy.Flag("name", "The cluster name (ie. staging, production) for the pod cluster that should be updated.").String() - updateStrategyID = cmdUpdateStrategy.Flag("id", "The UUID of the pod cluster that should be updated. This option is mutually exclusive with pod,az,name").String() - - // this flag specifies the strategy to update the pod cluster with - updateStrategy = cmdUpdateStrategy.Flag("strategy", "The strategy to set for the pod cluster").Required().Enum(rc_fields.StaticStrategy.String(), rc_fields.DynamicStrategy.String()) -) - -// "list" command -var ( - cmdList = kingpin.Command(cmdListText, "Lists pod clusters. ") -) - -func main() { - cmd, consulOpts, labeler := flags.ParseWithConsulOptions() - client := consul.NewConsulClient(consulOpts) - kv := consul.NewConsulStore(client) - logger := logging.NewLogger(logrus.Fields{}) - applicator := labels.NewConsulApplicator(client, 0, 1*time.Minute) - pcstore := pcstore.NewConsul(client, labeler, labels.DefaultAggregationRate, applicator, &logger) - - switch cmd { - case cmdCreateText: - az := fields.AvailabilityZone(*createAZ) - cn := fields.ClusterName(*createName) - podID := types.PodID(*createPodID) - selector := defaultSelector(az, cn, podID) - strategy := rc_fields.Strategy(*createStrategy) - pccontrol := control.NewPodCluster(az, cn, podID, pcstore, selector, strategy, 0) - - annotations := *createAnnotations - var parsedAnnotations map[string]interface{} - err := json.Unmarshal([]byte(annotations), &parsedAnnotations) - if err != nil { - log.Fatalf("could not parse json: %v", err) - } - - session, _, err := kv.NewSession(fmt.Sprintf("pcctl-%s", currentUserName()), nil) - if err != nil { - log.Fatalf("Could not create session: %s", err) - } - - _, err = pccontrol.Create(parsedAnnotations, session) - if err != nil { - log.Fatalf("err: %v", err) - } - case cmdGetText: - az := fields.AvailabilityZone(*getAZ) - cn := fields.ClusterName(*getName) - podID := types.PodID(*getPodID) - pcID := fields.ID(*getID) - - var pccontrol *control.PodCluster - if pcID != "" { - pccontrol = control.NewPodClusterFromID(pcID, pcstore) - } else if az != "" && cn != "" && podID != "" { - selector := defaultSelector(az, cn, podID) - pccontrol = control.NewPodCluster(az, cn, podID, pcstore, selector, "", 0) - } else { - log.Fatalf("Expected one of: pcID or (pod,az,name)") - } - - pc, err := pccontrol.Get() - if err != nil { - log.Fatalf("Caught error while fetching pod cluster: %v", err) - } - - bytes, err := json.Marshal(pc) - if err != nil { - logger.WithError(err).Fatalln("Unable to marshal PC as JSON") - } - fmt.Printf("%s", bytes) - case cmdDeleteText: - az := fields.AvailabilityZone(*deleteAZ) - cn := fields.ClusterName(*deleteName) - podID := types.PodID(*deletePodID) - pcID := fields.ID(*deleteID) - - var pccontrol *control.PodCluster - if pcID != "" { - pccontrol = control.NewPodClusterFromID(pcID, pcstore) - } else if az != "" && cn != "" && podID != "" { - selector := defaultSelector(az, cn, podID) - pccontrol = control.NewPodCluster(az, cn, podID, pcstore, selector, "", 0) - } else { - log.Fatalf("Expected one of: pcID or (pod,az,name)") - } - - errors := pccontrol.Delete() - if len(errors) >= 1 { - for _, err := range errors { - _, _ = os.Stderr.Write([]byte(fmt.Sprintf("Failed to delete one pod cluster matching arguments. Error:\n %s\n", err.Error()))) - } - os.Exit(1) - } - case cmdUpdateAnnotationsText: - az := fields.AvailabilityZone(*updateAnnotationsAZ) - cn := fields.ClusterName(*updateAnnotationsName) - podID := types.PodID(*updateAnnotationsPodID) - pcID := fields.ID(*updateAnnotationsID) - - var pccontrol *control.PodCluster - if pcID != "" { - pccontrol = control.NewPodClusterFromID(pcID, pcstore) - } else if az != "" && cn != "" && podID != "" { - selector := defaultSelector(az, cn, podID) - pccontrol = control.NewPodCluster(az, cn, podID, pcstore, selector, "", 0) - } else { - log.Fatalf("Expected one of: pcID or (pod,az,name)") - } - - var annotations fields.Annotations - err := json.Unmarshal([]byte(*updateAnnotations), &annotations) - if err != nil { - _, _ = os.Stderr.Write([]byte(fmt.Sprintf("Annotations are invalid JSON. Err follows:\n%v", err))) - os.Exit(1) - } - - pc, err := pccontrol.UpdateAnnotations(annotations) - if err != nil { - log.Fatalf("Error during PodCluster update: %v\n%v", err, pc) - os.Exit(1) - } - bytes, err := json.Marshal(pc) - if err != nil { - log.Fatalf("Update succeeded, but error during displaying PC: %v\n%+v", err, pc) - os.Exit(1) - } - fmt.Printf("%s", bytes) - case cmdUpdateStrategyText: - pc, err := podClusterFromParams( - fields.ID(*updateStrategyID), - types.PodID(*updateStrategyPodID), - fields.AvailabilityZone(*updateStrategyAZ), - fields.ClusterName(*updateStrategyName), - pcstore, - ) - if err != nil { - log.Fatal(err) - } - - pc, err = pcstore.MutatePC(pc.ID, func(pc fields.PodCluster) (fields.PodCluster, error) { - pc.AllocationStrategy = rc_fields.Strategy(*updateStrategy) - return pc, nil - }) - if err != nil { - log.Fatalf("Error during PodCluster update: %v\n%v", err, pc) - } - bytes, err := json.Marshal(pc) - if err != nil { - log.Fatalf("Update succeeded, but error during displaying PC: %v\n%+v", err, pc) - os.Exit(1) - } - fmt.Printf("%s", bytes) - case cmdUpdateSelectorText: - az := fields.AvailabilityZone(*updateSelectorAZ) - cn := fields.ClusterName(*updateSelectorName) - podID := types.PodID(*updateSelectorPodID) - pcID := fields.ID(*updateSelectorID) - - // no pccontrol for this one because we want to show a diff with CAS guarantees which is CLI specific - if pcID == "" { - if az == "" || cn == "" || podID == "" { - log.Fatal("you must specify a pod cluster ID or all of pod id, availability zone, and cluster name") - } else { - pcs, err := pcstore.FindWhereLabeled(podID, az, cn) - if err != nil { - log.Fatalf("could not search for pod cluster matching (%s, %s, %s): %s", podID, az, cn, err) - } - - if len(pcs) == 0 { - log.Fatalf("no pod cluster matched query (%s, %s, %s)", podID, az, cn) - } - - if len(pcs) > 1 { - // this should be impossible because of creation validation - log.Fatalf("multiple pod clusters matched query (%s, %s, %s)", podID, az, cn) - } - - pcID = pcs[0].ID - } - } - - newSelector, err := klabels.Parse(*updateSelector) - if err != nil { - log.Fatalf("could not parse %q as label selector: %s", *updateSelector, err) - } - - // Do the update within MutatePC. That way we know the update - // won't apply if anything about the pod cluster changes while - // we're showing the operator the label query diff - mutator := func(pc fields.PodCluster) (fields.PodCluster, error) { - oldSelector := pc.PodSelector - - err = confirmDiff(oldSelector, newSelector, applicator) - if err != nil { - log.Fatal(err) - } - - pc.PodSelector = newSelector - return pc, nil - } - - _, err = pcstore.MutatePC(pcID, mutator) - if err != nil { - log.Fatalf("could not apply pod cluster selector update: %s", err) - } - case cmdListText: - pcs, err := pcstore.List() - if err != nil { - _, _ = os.Stderr.Write([]byte(fmt.Sprintf("Could not list pcs. Err follows:\n%v", err))) - os.Exit(1) - } - - bytes, err := json.Marshal(pcs) - if err != nil { - _, _ = os.Stderr.Write([]byte(fmt.Sprintf("Could not marshal pc list. Err follows:\n%v", err))) - os.Exit(1) - } - fmt.Printf("%s", bytes) - default: - log.Fatalf("Unrecognized command %v", cmd) - } -} - -func defaultSelector(az fields.AvailabilityZone, cn fields.ClusterName, podID types.PodID) klabels.Selector { - return klabels.Everything(). - Add(fields.PodIDLabel, klabels.EqualsOperator, []string{podID.String()}). - Add(fields.AvailabilityZoneLabel, klabels.EqualsOperator, []string{az.String()}). - Add(fields.ClusterNameLabel, klabels.EqualsOperator, []string{cn.String()}) -} - -func currentUserName() string { - username := "unknown user" - - if user, err := user.Current(); err == nil { - username = user.Username - } - return username -} - -type Matcher interface { - GetMatches(selector klabels.Selector, labelType labels.Type) ([]labels.Labeled, error) -} - -func confirmDiff(oldSelector klabels.Selector, newSelector klabels.Selector, matcher Matcher) error { - oldPods, err := matcher.GetMatches(oldSelector, labels.POD) - if err != nil { - return fmt.Errorf("could not query pods using old selector: %s", err) - } - - newPods, err := matcher.GetMatches(newSelector, labels.POD) - if err != nil { - return fmt.Errorf("could not query pods using new selector: %s", err) - } - - addedPods, subtractedPods := computeDiff(oldPods, newPods) - if len(subtractedPods) != 0 { - fmt.Printf("WARNING: The following nodes will no longer be members of the pod cluster: %s\n", subtractedPods) - } - - if len(addedPods) != 0 { - fmt.Printf("The following nodes will be added as members of the pod cluster: %s\n", addedPods) - } - - if len(addedPods) == 0 && len(subtractedPods) == 0 { - fmt.Println("There will be no changes to pod cluster membership based on this selector change") - } - - fmt.Println("Do you wish to proceed?") - confirmed := cli.Confirm() - if !confirmed { - return errors.New("aborted") - } - - return nil -} - -func computeDiff(oldPods []labels.Labeled, newPods []labels.Labeled) ([]string, []string) { - var oldStrings, newStrings []string - for _, pod := range oldPods { - oldStrings = append(oldStrings, pod.ID) - } - - for _, pod := range newPods { - newStrings = append(newStrings, pod.ID) - } - - oldSet := sets.NewString(oldStrings...) - newSet := sets.NewString(newStrings...) - - added := newSet.Difference(oldSet) - removed := oldSet.Difference(newSet) - - return added.List(), removed.List() -} - -type PodClusterStore interface { - FindWhereLabeled( - podID types.PodID, - availabilityZone fields.AvailabilityZone, - clusterName fields.ClusterName, - ) ([]fields.PodCluster, error) - Get(id fields.ID) (fields.PodCluster, error) -} - -func podClusterFromParams( - id fields.ID, - podID types.PodID, - az fields.AvailabilityZone, - cn fields.ClusterName, - pcStore PodClusterStore, -) (fields.PodCluster, error) { - if id != "" { - return pcStore.Get(id) - } - - if az == "" || cn == "" || podID == "" { - return fields.PodCluster{}, errors.New("Expected one of: pcID or (pod,az,name)") - } - - pcs, err := pcStore.FindWhereLabeled(podID, az, cn) - if err != nil { - return fields.PodCluster{}, err - } - - if len(pcs) != 1 { - return fields.PodCluster{}, fmt.Errorf("found %d pod clusters that match", len(pcs)) - } - - return pcs[0], nil -} diff --git a/bin/p2-replicate/main.go b/bin/p2-replicate/main.go deleted file mode 100644 index 445d96bad..000000000 --- a/bin/p2-replicate/main.go +++ /dev/null @@ -1,130 +0,0 @@ -package main - -import ( - "fmt" - "log" - "os" - "os/signal" - "os/user" - "time" - - "github.com/sirupsen/logrus" - "gopkg.in/alecthomas/kingpin.v2" - - "github.com/square/p2/pkg/health" - "github.com/square/p2/pkg/health/checker" - "github.com/square/p2/pkg/logging" - "github.com/square/p2/pkg/manifest" - "github.com/square/p2/pkg/replication" - "github.com/square/p2/pkg/store/consul" - "github.com/square/p2/pkg/store/consul/flags" - "github.com/square/p2/pkg/types" - "github.com/square/p2/pkg/version" -) - -var ( - manifestURI = kingpin.Arg("manifest", "a path or url to a pod manifest that will be replicated.").Required().URL() - hosts = kingpin.Arg("hosts", "Hosts to replicate to").Required().Strings() - minNodes = kingpin.Flag("min-nodes", "The minimum number of healthy nodes that must remain up while replicating.").Default("1").Short('m').Int() - threshold = kingpin.Flag("threshold", "The minimum health level to treat as healthy. One of (in order) passing, warning, unknown, critical.").String() - overrideLock = kingpin.Flag("override-lock", "Override any lock holders").Bool() - ignoreControllers = kingpin.Flag("ignore-controllers", "Deploy even if there are controllers managing some of the hosts").Bool() - concurrentRealityChecks = kingpin.Flag("concurrent-reality-checks", "The number of concurrent requests to check for reality state (this is one area where p2-replicate does not use long-lived watches)").Default(fmt.Sprintf("%v", replication.DefaultConcurrentReality)).Int() -) - -func main() { - kingpin.CommandLine.Name = "p2-replicate" - kingpin.CommandLine.Help = `p2-replicate uses the replication package to schedule deployment of a pod across multiple nodes. See the replication package's README and godoc for more information. - - Example invocation: p2-replicate --min-nodes 2 helloworld.yaml aws{1,2,3}.example.com - - This will take the pod whose manifest is located at helloworld.yaml and - deploy it to the three nodes aws1.example.com, aws2.example.com, and - aws3.example.com - - Because of --min-nodes 2, the replicator will ensure that at least two healthy - nodes remain up at all times, according to p2's health checks. -` - - kingpin.Version(version.VERSION) - _, opts, labeler := flags.ParseWithConsulOptions() - client := consul.NewConsulClient(opts) - store := consul.NewConsulStore(client) - healthChecker := checker.NewHealthChecker(client) - - manifest, err := manifest.FromURI(*manifestURI) - if err != nil { - log.Fatalf("%s", err) - } - - logger := logging.NewLogger(logrus.Fields{ - "pod": manifest.ID(), - }) - logger.Logger.Formatter = &logrus.TextFormatter{ - DisableTimestamp: false, - FullTimestamp: true, - TimestampFormat: "15:04:05.000", - } - - // create a lock with a meaningful name and set up a renewal loop for it - thisHost, err := os.Hostname() - if err != nil { - log.Fatalf("Could not retrieve hostname: %s", err) - } - thisUser, err := user.Current() - if err != nil { - log.Fatalf("Could not retrieve user: %s", err) - } - - nodes := make([]types.NodeName, len(*hosts)) - for i, host := range *hosts { - nodes[i] = types.NodeName(host) - } - - lockMessage := fmt.Sprintf("%q from %q at %q", thisUser.Username, thisHost, time.Now()) - repl, err := replication.NewReplicator( - manifest, - logger, - nodes, - len(*hosts)-*minNodes, - store, - client.KV(), - labeler, - healthChecker, - health.HealthState(*threshold), - lockMessage, - replication.NoTimeout, - 1*time.Second, - ) - if err != nil { - log.Fatalf("Could not initialize replicator: %s", err) - } - - replication, errCh, err := repl.InitializeReplication( - *overrideLock, - *ignoreControllers, - *concurrentRealityChecks, - 0, - nil, - ) - if err != nil { - log.Fatalf("Unable to initialize replication: %s", err) - } - - // auto-drain this channel - go func() { - for range errCh { - } - }() - - go func() { - // clear lock immediately on ctrl-C - signals := make(chan os.Signal, 1) - signal.Notify(signals, os.Interrupt) - <-signals - replication.Cancel() - os.Exit(1) - }() - - replication.Enact() -} diff --git a/bin/p2-rm/main.go b/bin/p2-rm/main.go deleted file mode 100644 index 11fb7ba08..000000000 --- a/bin/p2-rm/main.go +++ /dev/null @@ -1,109 +0,0 @@ -// p2-rm is a command line tool for removing a pods and its labels. -package main - -import ( - "fmt" - "os" - "os/user" - - "gopkg.in/alecthomas/kingpin.v2" - - "github.com/square/p2/pkg/labels" - "github.com/square/p2/pkg/rc/fields" - "github.com/square/p2/pkg/store/consul" - "github.com/square/p2/pkg/store/consul/consulutil" - "github.com/square/p2/pkg/store/consul/flags" - "github.com/square/p2/pkg/types" - "github.com/square/p2/pkg/version" -) - -// Command line flags -var ( - podName = kingpin.Arg("pod", "The names of the pod to be removed").String() - nodeName = kingpin.Flag("node", "The node to unschedule the pod from. Uses the hostname by default. Only applies to \"legacy\" pods.").String() - podUniqueKey = kingpin.Flag("pod-unique-key", "The pod unique key to unschedule. Only applies to \"uuid\" pods. Cannot be used with --node").Short('k').String() - deallocation = kingpin.Flag("deallocate", "Specifies that we are deallocating this pod on this node. Using this switch will mutate the desired_replicas value on a managing RC, if one exists.").Bool() - removeOrphan = kingpin.Flag("remove-orphan", "Remove the pod even if it is labeled with a replication controller ID, but only if no RC with that ID exists").Bool() -) - -func main() { - kingpin.Version(version.VERSION) - _, opts, _ := flags.ParseWithConsulOptions() - - consulClient := consul.NewConsulClient(opts) - - // we ignore the labels.ApplicatorWithoutWatches that - // ParseWithConsulOptions() gives us because the RC store now requires - // transactions which that interface does not provide - labeler := labels.NewConsulApplicator(consulClient, 0, 0) - - err := handlePodRemoval(consulClient, labeler) - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } -} - -func handlePodRemoval(consulClient consulutil.ConsulClient, labeler Labeler) error { - var rm *P2RM - if *podUniqueKey != "" { - rm = NewUUIDP2RM(consulClient, types.PodUniqueKey(*podUniqueKey), types.PodID(*podName), labeler) - } else { - if *podName == "" { - return fmt.Errorf("pod argument is required when removing a legacy pod") - } - - if *nodeName == "" { - hostname, err := os.Hostname() - if err != nil { - return fmt.Errorf("error getting hostname. use --node to specify a node: %v\n", err) - } - *nodeName = hostname - } - - rm = NewLegacyP2RM(consulClient, types.PodID(*podName), types.NodeName(*nodeName), labeler) - } - - podIsManagedByRC, rcID, err := rm.checkForManagingReplicationController(*removeOrphan) - if err != nil { - return err - } - - if !podIsManagedByRC { - err = rm.deletePod() - if err != nil { - return err - } - } - - if podIsManagedByRC && !*deallocation { - return fmt.Errorf("error: %s is managed by replication controller: %s\n"+ - "It's possible you meant you deallocate this pod on this node. If so, please confirm your intention with --deallocate\n", *nodeName, rcID) - } - - if podIsManagedByRC && *deallocation { - err = rm.decrementDesiredCount(rcID) - if err != nil { - return fmt.Errorf("Encountered error deallocating from the RC %s. You may attempt this command again or use `p2-rctl` to cleanup manually.\n%v", - rcID, - err) - } - } - - if rm.NodeName != "" { - fmt.Printf("%s: successfully removed %s\n", rm.NodeName, rm.PodID) - } else { - fmt.Printf("successfully removed %s-%s\n", rm.PodID, rm.PodUniqueKey) - } - return nil -} - -func sessionName(rcID fields.ID) string { - currentUser, err := user.Current() - username := "unknown" - if err == nil { - username = currentUser.Username - } - - return fmt.Sprintf("p2-rm:user:%s:rcID:%s", username, rcID) -} diff --git a/bin/p2-rm/rm.go b/bin/p2-rm/rm.go deleted file mode 100644 index 806623a2d..000000000 --- a/bin/p2-rm/rm.go +++ /dev/null @@ -1,225 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "path" - "time" - - "github.com/square/p2/pkg/cli" - "github.com/square/p2/pkg/labels" - "github.com/square/p2/pkg/rc" - "github.com/square/p2/pkg/rc/fields" - "github.com/square/p2/pkg/store/consul" - "github.com/square/p2/pkg/store/consul/consulutil" - "github.com/square/p2/pkg/store/consul/podstore" - "github.com/square/p2/pkg/store/consul/rcstore" - "github.com/square/p2/pkg/types" -) - -type store interface { - NewSession(name string, renewalCh <-chan time.Time) (consul.Session, chan error, error) - DeletePod(podPrefix consul.PodPrefix, nodename types.NodeName, podId types.PodID) (time.Duration, error) -} - -type ReplicationControllerLocker interface { - LockForMutation(rcID fields.ID, session consul.Session) (consul.Unlocker, error) -} - -type ReplicationControllerStore interface { - Disable(id fields.ID) error - AddDesiredReplicas(id fields.ID, n int) error - Enable(id fields.ID) error - Get(id fields.ID) (fields.RC, error) -} - -type Labeler interface { - rcstore.RCLabeler - GetLabels(labels.Type, string) (labels.Labeled, error) -} - -type P2RM struct { - Store store - RCLocker ReplicationControllerLocker - RCStore ReplicationControllerStore - Client consulutil.ConsulClient - Labeler Labeler - PodStore podstore.Store - - LabelID string - NodeName types.NodeName - PodID types.PodID - PodUniqueKey types.PodUniqueKey -} - -// NewLegacyP2RM is a constructor for the P2RM type which configures it to -// remove a "legacy" pod. It will generate the storage types based on its -// api.Client argument -func NewLegacyP2RM(client consulutil.ConsulClient, podName types.PodID, nodeName types.NodeName, labeler Labeler) *P2RM { - rm := &P2RM{} - rm.LabelID = path.Join(nodeName.String(), podName.String()) - rm.PodID = podName - rm.NodeName = nodeName - rm.PodUniqueKey = "" - rm.configureStorage(client, labeler) - return rm -} - -// Constructs a *P2RM configured to remove a pod identified by a PodUniqueKey (uuid) -func NewUUIDP2RM(client consulutil.ConsulClient, podUniqueKey types.PodUniqueKey, podID types.PodID, labeler Labeler) *P2RM { - rm := &P2RM{} - rm.LabelID = podUniqueKey.String() - rm.PodID = podID - rm.NodeName = "" // don't need node name to look up a uuid pod - rm.PodUniqueKey = podUniqueKey - rm.configureStorage(client, labeler) - return rm -} - -func (rm *P2RM) configureStorage(client consulutil.ConsulClient, labeler Labeler) { - rm.Client = client - rm.Store = consul.NewConsulStore(client) - consulStore := rcstore.NewConsul(client, labeler, 5) - - // one day these might have different implementations - rm.RCStore = consulStore - rm.RCLocker = consulStore - - rm.Labeler = labeler - rm.PodStore = podstore.NewConsul(client.KV()) -} - -func (rm *P2RM) checkForManagingReplicationController(checkForOrphaned bool) (bool, fields.ID, error) { - podLabels, err := rm.Labeler.GetLabels(labels.POD, rm.LabelID) - if err != nil { - return false, "", fmt.Errorf("unable to check node for labels: %v", err) - } - - if podLabels.Labels.Has(rc.RCIDLabel) { - if checkForOrphaned { - _, err := rm.RCStore.Get(fields.ID(podLabels.Labels[rc.RCIDLabel])) - switch { - case err == rcstore.NoReplicationController: - // this pod is an orphan, so return false, allowing it to be removed - return false, "", nil - case err != nil: - return true, fields.ID(podLabels.Labels.Get(rc.RCIDLabel)), fmt.Errorf("could not check if RC exists: %s", err) - } - } - - return true, fields.ID(podLabels.Labels.Get(rc.RCIDLabel)), nil - } - - return false, "", nil -} - -func (rm *P2RM) decrementDesiredCount(id fields.ID) error { - session, _, err := rm.Store.NewSession(sessionName(id), nil) - if err != nil { - return fmt.Errorf("Unable to get consul session: %v", err) - } - - rcLock, err := rm.RCLocker.LockForMutation(id, session) - if err != nil { - return fmt.Errorf("Unable to lock RC for mutation: %v", err) - } - defer rcLock.Unlock() - - err = rm.RCStore.Disable(id) - if err != nil { - return fmt.Errorf("Could not disable RC %s: %v", id, err) - } - - err = rm.deletePod() - if err != nil { - return fmt.Errorf("Unable to delete pod. Please re-run this command to clean up: %v", err) - } - - err = rm.RCStore.AddDesiredReplicas(id, -1) - if err != nil { - return fmt.Errorf("Unable to decrement RC count: %v", err) - } - - err = rm.RCStore.Enable(id) - if err != nil { - return fmt.Errorf("Could not enable RC %s: %v", id, err) - } - - if err = session.Destroy(); err != nil { - return fmt.Errorf("Unable to destroy consul session: %v", err) - } - - return nil -} - -func (rm *P2RM) deletePod() error { - if rm.PodUniqueKey == "" { - err := rm.deleteLegacyPod() - if err != nil { - return err - } - } else { - err := rm.deleteUUIDPod() - if err != nil { - return err - } - } - - return rm.removePodLabels() -} - -func (rm *P2RM) deleteLegacyPod() error { - _, err := rm.Store.DeletePod(consul.INTENT_TREE, rm.NodeName, types.PodID(rm.PodID)) - if err != nil { - return fmt.Errorf("unable to remove pod: %v", err) - } - err = rm.Labeler.RemoveAllLabels(labels.POD, labels.MakePodLabelKey(rm.NodeName, rm.PodID)) - if err != nil { - return fmt.Errorf("unalbe to remove labels: %s", err) - } - - return nil -} - -func (rm *P2RM) deleteUUIDPod() error { - // Sanity check that the passed pod ID matches the pod being deleted - pod, err := rm.PodStore.ReadPod(rm.PodUniqueKey) - switch { - case err == nil: - if rm.PodID != "" { - if pod.Manifest.ID() != rm.PodID { - return fmt.Errorf("pod %s has a podID of %s, but %s was passed as the --pod option", rm.PodUniqueKey, pod.Manifest.ID(), rm.PodID) - } - } else { - fmt.Println(fmt.Sprintf("%s has pod ID of %s, do you wish to proceed?", rm.PodUniqueKey, pod.Manifest.ID())) - confirmed := cli.Confirm() - if !confirmed { - return errors.New("aborted") - } - } - - err = rm.PodStore.Unschedule(rm.PodUniqueKey) - if err != nil { - return fmt.Errorf("Unable to unschedule pod: %s", err) - } - case podstore.IsNoPod(err): - // This is okay, the command might be re-run to flush out label deletion errors - case err != nil: - return fmt.Errorf("Could not verify that pod %s matches pod ID of %s: %s", rm.PodUniqueKey, rm.PodID, err) - } - - return nil -} - -func (rm *P2RM) removePodLabels() error { - err := rm.Labeler.RemoveAllLabels(labels.POD, rm.LabelID) - if err != nil { - return fmt.Errorf( - "pod is partially deleted. re-run command to finish deleting\n"+ - "unable to remove pod labels: %v", - err, - ) - } - - return nil -}