Skip to content

Commit

Permalink
Merge pull request #383 from digitalocean/k8s-default-to-usable-setting
Browse files Browse the repository at this point in the history
k8s: default to usable settings
  • Loading branch information
hilary authored Dec 9, 2018
2 parents fc78aff + e779eff commit bf828cc
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 29 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All changes to `doctl` will be documented in this file.

## [1.12.2] - UNRELEASED

- #383 Fix bad default for cluster node sizes, improve help and warn of kubeconfig expiry date.

## [1.12.1] - 2018-12-09

- #354 volumes: Fix droplet ID display when listing volumes - @adamwg
Expand Down
2 changes: 2 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const (
ArgClusterVersionSlug = "version"
// ArgClusterNodePool are a cluster's node pools arguments.
ArgClusterNodePool = "node-pool"
// ArgClusterTag is a cluster's tags arguments.
ArgClusterTag = "tag"
// ArgClusterUpdateKubeconfig updates the local kubeconfig.
ArgClusterUpdateKubeconfig = "update-kubeconfig"
// ArgNodePoolName is a cluster's node pool name argument.
Expand Down
89 changes: 60 additions & 29 deletions commands/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ package commands

import (
"context"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"os"
Expand All @@ -38,6 +40,11 @@ import (
const (
maxAPIFailures = 5
timeoutFetchingKubeconfig = 30 * time.Second

defaultKubernetesNodeSize = "s-1vcpu-2gb"
defaultKubernetesNodeCount = 3
defaultKubernetesRegion = "nyc1"
defaultKubernetesLatestVersion = "latest"
)

func errNoClusterByName(name string) error {
Expand Down Expand Up @@ -83,12 +90,6 @@ func Kubernetes() *Command {

func kubernetesCluster() *Command {

const (
defaultNodeSize = "s-1vcpu-1gb"
defaultNodeCount = 3
defaultRegion = "nyc1"
)

cmd := &Command{
Command: &cobra.Command{
Use: "cluster",
Expand All @@ -105,23 +106,28 @@ func kubernetesCluster() *Command {
CmdBuilder(cmd, RunKubernetesClusterGet, "get <id|name>", "get a cluster", Writer, aliasOpt("g"))
CmdBuilder(cmd, RunKubernetesClusterList, "list", "get a list of your clusters", Writer, aliasOpt("ls"))

cmdKubeClusterCreate := CmdBuilder(cmd, RunKubernetesClusterCreate(defaultNodeSize, defaultNodeCount), "create <name>", "create a cluster", Writer, aliasOpt("c"))
AddStringFlag(cmdKubeClusterCreate, doctl.ArgRegionSlug, "", defaultRegion, "cluster region, possible values: see `doctl k8s options regions`.", requiredOpt())
AddStringFlag(cmdKubeClusterCreate, doctl.ArgClusterVersionSlug, "", "", "cluster version, possible values: see `doctl k8s options versions`")
AddStringSliceFlag(cmdKubeClusterCreate, doctl.ArgTagNames, "", nil, "cluster tags")
AddStringFlag(cmdKubeClusterCreate, doctl.ArgSizeSlug, "", defaultNodeSize, "size of the nodes in the default node pool (incompatible with --"+doctl.ArgClusterNodePool+"), possible values: see `doctl k8s options sizes`.")
AddStringFlag(cmdKubeClusterCreate, doctl.ArgNodePoolCount, "", strconv.Itoa(defaultNodeCount), "number of nodes in the default node pool (incompatible with --"+doctl.ArgClusterNodePool+")")
AddStringSliceFlag(cmdKubeClusterCreate, doctl.ArgClusterNodePool, "", nil, `cluster node pools in the form "name=your-name;size=droplet_size;count=5;tag=tag1;tag=tag2"`, requiredOpt())
cmdKubeClusterCreate := CmdBuilder(cmd, RunKubernetesClusterCreate(defaultKubernetesNodeSize, defaultKubernetesNodeCount), "create <name>", "create a cluster", Writer, aliasOpt("c"))
AddStringFlag(cmdKubeClusterCreate, doctl.ArgRegionSlug, "", defaultKubernetesRegion, `cluster region, possible values: see "doctl k8s options regions"`, requiredOpt())
AddStringFlag(cmdKubeClusterCreate, doctl.ArgClusterVersionSlug, "", "latest", `cluster version, possible values: see "doctl k8s options versions"`)
AddStringSliceFlag(cmdKubeClusterCreate, doctl.ArgClusterTag, "", nil, "tags to apply to the cluster, repeat to add multiple tags at once")
AddStringFlag(cmdKubeClusterCreate, doctl.ArgSizeSlug, "", defaultKubernetesNodeSize, `size of nodes in the default node pool (incompatible with --`+doctl.ArgClusterNodePool+`), possible values: see "doctl k8s options sizes".`)
AddStringFlag(cmdKubeClusterCreate, doctl.ArgNodePoolCount, "", strconv.Itoa(defaultKubernetesNodeCount), "number of nodes in the default node pool (incompatible with --"+doctl.ArgClusterNodePool+")")
AddStringSliceFlag(cmdKubeClusterCreate, doctl.ArgClusterNodePool, "", nil, `cluster node pools, can be repeated to create multiple node pools at once (incompatible with --`+doctl.ArgSizeSlug+` and --`+doctl.ArgNodePoolCount+`)
format is in the form "name=your-name;size=size_slug;count=5;tag=tag1;tag=tag2" where:
- name: name of the node pool, must be unique in the cluster
- size: size for the nodes in the node pool, possible values: see "doctl k8s options sizes".
- count: number of nodes in the node pool.
- tag: tags to apply to the node pool, repeat to add multiple tags at once.`)
AddBoolFlag(cmdKubeClusterCreate, doctl.ArgClusterUpdateKubeconfig, "", true, "whether to add the created cluster to your kubeconfig")
AddBoolFlag(cmdKubeClusterCreate, doctl.ArgCommandWait, "", true, "whether to wait for the created cluster become running")
AddBoolFlag(cmdKubeClusterCreate, doctl.ArgCommandWait, "", true, "whether to wait for the created cluster to become running")

cmdKubeClusterUpdate := CmdBuilder(cmd, RunKubernetesClusterUpdate, "update <id|name>", "update a cluster's properties", Writer, aliasOpt("u"))
AddStringFlag(cmdKubeClusterUpdate, doctl.ArgClusterName, "", "", "new cluster name")
AddStringSliceFlag(cmdKubeClusterUpdate, doctl.ArgTagNames, "", nil, "new cluster tags")
AddStringSliceFlag(cmdKubeClusterUpdate, doctl.ArgClusterTag, "", nil, "tags to apply to the cluster, repeat to add multiple tags at once")
AddBoolFlag(cmdKubeClusterUpdate, doctl.ArgClusterUpdateKubeconfig, "", true, "whether to update the cluster in your kubeconfig")

cmdKubeClusterDelete := CmdBuilder(cmd, RunKubernetesClusterDelete, "delete <id|name>", "delete a cluster", Writer, aliasOpt("d", "rm"))
AddBoolFlag(cmdKubeClusterDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Force cluster delete")
AddBoolFlag(cmdKubeClusterDelete, doctl.ArgForce, doctl.ArgShortForce, false, "force cluster delete")
AddBoolFlag(cmdKubeClusterDelete, doctl.ArgClusterUpdateKubeconfig, "", true, "whether to remove the deleted cluster to your kubeconfig")

return cmd
Expand Down Expand Up @@ -160,18 +166,18 @@ func kubernetesNodePools() *Command {
AddStringFlag(cmdKubeNodePoolCreate, doctl.ArgNodePoolName, "", "", "node pool name", requiredOpt())
AddStringFlag(cmdKubeNodePoolCreate, doctl.ArgSizeSlug, "", "", "size of nodes in the node pool (see `doctl k8s options sizes`)", requiredOpt())
AddStringFlag(cmdKubeNodePoolCreate, doctl.ArgNodePoolCount, "", "", "count of nodes in the node pool", requiredOpt())
AddStringFlag(cmdKubeNodePoolCreate, doctl.ArgTagNames, "", "", "tags to apply to the node pool")
AddStringFlag(cmdKubeNodePoolCreate, doctl.ArgClusterTag, "", "", "tags to apply to the node pool, repeat to add multiple tags at once")

cmdKubeNodePoolUpdate := CmdBuilder(cmd, RunKubernetesNodePoolUpdate, "update <cluster-id|cluster-name> <pool-id|pool-name>", "update an existing node pool in a cluster", Writer, aliasOpt("u"))
AddStringFlag(cmdKubeNodePoolUpdate, doctl.ArgNodePoolName, "", "", "node pool name")
AddStringFlag(cmdKubeNodePoolUpdate, doctl.ArgNodePoolCount, "", "", "count of nodes in the node pool")
AddStringFlag(cmdKubeNodePoolUpdate, doctl.ArgTagNames, "", "", "tags to apply to the node pool")
AddStringFlag(cmdKubeNodePoolUpdate, doctl.ArgClusterTag, "", "", "tags to apply to the node pool, repeat to add multiple tags at once")

cmdKubeNodePoolRecycle := CmdBuilder(cmd, RunKubernetesNodePoolRecycle, "recycle <cluster-id|cluster-name> <pool-id|pool-name>", "recycle nodes in a node pool", Writer, aliasOpt("r"))
AddStringFlag(cmdKubeNodePoolRecycle, doctl.ArgNodePoolNodeIDs, "", "", "ID or name of the nodes in the node pool to recycle")

cmdKubeNodePoolDelete := CmdBuilder(cmd, RunKubernetesNodePoolDelete, "delete <cluster-id|cluster-name> <pool-id|pool-name>", "delete node pool from a cluster", Writer, aliasOpt("d", "rm"))
AddBoolFlag(cmdKubeNodePoolDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Force node pool delete")
AddBoolFlag(cmdKubeNodePoolDelete, doctl.ArgForce, doctl.ArgShortForce, false, "force node pool delete")
return cmd
}

Expand Down Expand Up @@ -247,7 +253,7 @@ func RunKubernetesClusterCreate(defaultNodeSize string, defaultNodeCount int) fu

if update {
notice("cluster created, fetching credentials")
tryUpdateKubeconfig(kube, cluster.ID)
tryUpdateKubeconfig(kube, cluster.ID, clusterName)
}

if wait {
Expand All @@ -271,7 +277,8 @@ func RunKubernetesClusterUpdate(c *CmdConfig) error {
if err != nil {
return err
}
clusterID, err := clusterIDize(c.Kubernetes(), c.Args[0])
clusterIDorName := c.Args[0]
clusterID, err := clusterIDize(c.Kubernetes(), clusterIDorName)
if err != nil {
return err
}
Expand All @@ -289,13 +296,13 @@ func RunKubernetesClusterUpdate(c *CmdConfig) error {

if update {
notice("cluster updated, fetching new credentials")
tryUpdateKubeconfig(kube, clusterID)
tryUpdateKubeconfig(kube, clusterID, clusterIDorName)
}

return displayClusters(c, true, *cluster)
}

func tryUpdateKubeconfig(kube do.KubernetesService, clusterID string) {
func tryUpdateKubeconfig(kube do.KubernetesService, clusterID, clusterName string) {
var (
kubeconfig []byte
err error
Expand All @@ -315,7 +322,7 @@ func tryUpdateKubeconfig(kube do.KubernetesService, clusterID string) {
break
}
}
if err := writeOrAddToKubeconfig(kubeconfig); err != nil {
if err := writeOrAddToKubeconfig(clusterName, kubeconfig); err != nil {
warn("couldn't write cluster credentials: %v", err)
}
}
Expand Down Expand Up @@ -394,6 +401,7 @@ func RunKubernetesKubeconfigSave(c *CmdConfig) error {
return doctl.NewMissingArgsErr(c.NS)
}
kube := c.Kubernetes()
idOrName := c.Args[0]
clusterID, err := clusterIDize(kube, c.Args[0])
if err != nil {
return err
Expand All @@ -402,7 +410,7 @@ func RunKubernetesKubeconfigSave(c *CmdConfig) error {
if err != nil {
return err
}
if err := writeOrAddToKubeconfig(kubeconfig); err != nil {
if err := writeOrAddToKubeconfig(idOrName, kubeconfig); err != nil {
return err
}
return nil
Expand Down Expand Up @@ -806,7 +814,7 @@ func buildNodePoolUpdateRequestFromArgs(c *CmdConfig, r *godo.KubernetesNodePool
return nil
}

func writeOrAddToKubeconfig(kubeconfig []byte) error {
func writeOrAddToKubeconfig(clusterIDOrName string, kubeconfig []byte) error {
remote, err := clientcmd.Load(kubeconfig)
if err != nil {
return err
Expand All @@ -816,8 +824,27 @@ func writeOrAddToKubeconfig(kubeconfig []byte) error {
if err != nil {
return err
}
catchExpiryDate := func(ai *clientcmdapi.AuthInfo) {
if len(ai.ClientCertificateData) == 0 {
return // auth info is probably not certificate based
}
block, _ := pem.Decode(ai.ClientCertificateData)
c, err := x509.ParseCertificate(block.Bytes)
if err != nil {
warn("bad certificate: %v", err)
return // auth info is probably not certificate based
}
if c.NotAfter.IsZero() {
return // doesn't expire
}
warn(
`cluster credentials will expire on %v, renew with: "doctl k8s kubeconfig save %s"`,
c.NotAfter.Format("2006-01-02 15:04:05 MST"),
clusterIDOrName,
)
}
notice("adding cluster credentials to kubeconfig file found in %q", kubectlDefaults.GlobalFile)
if err := mergeKubeconfig(remote, currentConfig); err != nil {
if err := mergeKubeconfig(remote, currentConfig, catchExpiryDate); err != nil {
return fmt.Errorf("couldn't use the kubeconfig info received, %v", err)
}
return clientcmd.ModifyConfig(kubectlDefaults, *currentConfig, false)
Expand All @@ -843,7 +870,7 @@ func removeFromKubeconfig(kubeconfig []byte) error {
// mergeKubeconfig merges a remote cluster's config file with a local config file,
// assuming that the current context in the remote config file points to the
// cluster details to add to the local config.
func mergeKubeconfig(remote, local *clientcmdapi.Config) error {
func mergeKubeconfig(remote, local *clientcmdapi.Config, authInfoCb func(*clientcmdapi.AuthInfo)) error {
remoteCtx, ok := remote.Contexts[remote.CurrentContext]
if !ok {
// this is a bug in the backend, we received incomplete/non-sensical data
Expand All @@ -866,6 +893,10 @@ func mergeKubeconfig(remote, local *clientcmdapi.Config) error {
)
}

if authInfoCb != nil {
authInfoCb(remoteAuthInfo)
}

local.Contexts[remote.CurrentContext] = remoteCtx
local.Clusters[remoteCtx.Cluster] = remoteCluster
local.AuthInfos[remoteCtx.AuthInfo] = remoteAuthInfo
Expand Down Expand Up @@ -1124,7 +1155,7 @@ func getVersionOrLatest(c *CmdConfig) (string, error) {
if err != nil {
return "", err
}
if version != "" {
if version != "" && version != defaultKubernetesLatestVersion {
return version, nil
}
versions, err := c.Kubernetes().GetVersions()
Expand Down

0 comments on commit bf828cc

Please sign in to comment.