Skip to content

Commit

Permalink
add new kubectl_version param (#115)
Browse files Browse the repository at this point in the history
* add new `kubectl_version` param

- enables running the plugin using a non-default `kubectl` executable
- updates image `ENTRYPOINT` to support setting of `EXTRA_KUBECTL_VERSIONS` environment variable used by plugin to validate against `kubectl_version`

depends on #114

* update DOCS

* update validation implementation, docs; add new option to make run target

* rename entrypoint to set-env-versions
  • Loading branch information
montmanu authored Jul 31, 2019
1 parent 6c66920 commit 01d7120
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 5 deletions.
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ export PLUGIN_VARS="$(cat ${CONFIG_HOME}/custom-vars.json)"
make run
```

Using custom kubectl version:

```sh
export PLUGIN_KUBECTL_VERSION=1.14
make run
```

If you've built the docker image using a custom repo or tag:

```sh
Expand Down
60 changes: 60 additions & 0 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,29 @@ pipeline:
# ...
```

### `kubectl_version`

_**type**_ `string`

_**default**_ `''`

_**description**_ version of kubectl executable to use

_**notes**_ see [Using "extra" `kubectl` versions](#using-extra-kubectl-versions) for details

_**example**_

```yaml
# .drone.yml
---
pipeline:
# ...
deploy:
image: nytimes/drone-gke
kubectl_version: "1.14"
# ...
```

### `dry_run`

_**type**_ `bool`
Expand Down Expand Up @@ -461,6 +484,43 @@ To use `$${IMAGE_VERSION}` or `$IMAGE_VERSION`, see the [Drone docs][environment
[expand]: https://golang.org/pkg/os/#ExpandEnv
[environment]: http://docs.drone.io/environment/

## Using "extra" `kubectl` versions

### tl;dr

To run `drone-gke` using a different version of `kubectl` than the default, set `kubectl-version` to the version you'd like to use.

For example, to use the **1.14** version of `kubectl`:

```yml
image: nytimes/drone-gke
kubectl_version: "1.14"
```

This will configure the plugin to execute `/google-cloud-sdk/bin/kubectl.1.14` instead of `/google-cloud-sdk/bin/kubectl` for all `kubectl` commands.

### Background

Beginning with the [`237.0.0 (2019-03-05)` release of the `gcloud` SDK](https://cloud.google.com/sdk/docs/release-notes#23700_2019-03-05), "extra" `kubectl` versions are installed automatically when `kubectl` is installed via `gcloud components install kubectl`.

These "extra" versions are installed alongside the SDK's default `kubectl` version at `/google-cloud-sdk/bin` and are named using the following pattern:

```sh
kubectl.$clientVersionMajor.$clientVersionMinor
```

To list all of the "extra" `kubectl` versions available within a particular version of `drone-gke`, you can run the following:

```sh
# list "extra" kubectl versions available with nytimes/drone-gke
docker run \
--rm \
--interactive \
--tty \
--entrypoint '' \
nytimes/drone-gke list-extra-kubectl-versions
```

## Example reference usage

### `.drone.yml`
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ RUN \
gcloud --no-user-output-enabled components install kubectl && \
rm -rf /google-cloud-sdk/.install

ADD drone-gke /usr/local/bin/
ADD drone-gke bin/set-env-versions bin/list-extra-kubectl-versions /usr/local/bin/

ENTRYPOINT ["drone-gke"]
ENTRYPOINT ["set-env-versions", "drone-gke"]
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ run :
--env PLUGIN_CLUSTER \
--env PLUGIN_DRY_RUN \
--env PLUGIN_EXPAND_ENV_VARS \
--env PLUGIN_KUBECTL_VERSION \
--env PLUGIN_NAMESPACE \
--env PLUGIN_PROJECT \
--env PLUGIN_REGION \
Expand Down
14 changes: 14 additions & 0 deletions bin/list-extra-kubectl-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env sh
set -e

# location to search for extra kubectl version
# defaults to default used by `gcloud components install kubectl`
gcloud_sdk_bin_dir=${1:-'/google-cloud-sdk/bin'}

# filename glob for extra kubectl versions
# defaults to default used by `gcloud components install kubectl`
extra_kubectl_glob=${2:-'kubectl.*'}

find ${gcloud_sdk_bin_dir} -type f -name ${extra_kubectl_glob} -exec basename {} \; \
| cut -d '.' -f 2,3 \
| sort -g
6 changes: 6 additions & 0 deletions bin/set-env-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env sh
set -e

export EXTRA_KUBECTL_VERSIONS=$(echo $(list-extra-kubectl-versions))

exec "$@"
48 changes: 45 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,18 @@ var (
)

const (
gcloudCmd = "gcloud"
kubectlCmd = "kubectl"
timeoutCmd = "timeout"
gcloudCmd = "gcloud"
kubectlCmdName = "kubectl"
timeoutCmd = "timeout"

keyPath = "/tmp/gcloud.json"
nsPath = "/tmp/namespace.json"
templateBasePath = "/tmp"
)

// default to kubectlCmdName, can be overriden via kubectl-version param
var kubectlCmd = kubectlCmdName
var extraKubectlVersions = strings.Split(os.Getenv("EXTRA_KUBECTL_VERSIONS"), " ")
var nsTemplate = `
---
apiVersion: v1
Expand Down Expand Up @@ -167,6 +170,12 @@ func wrapMain() error {
EnvVar: "PLUGIN_WAIT_SECONDS",
Value: 0,
},
cli.StringFlag{
Name: "kubectl-version",
Usage: "optional - version of kubectl binary to use, e.g. 1.14",
EnvVar: "PLUGIN_KUBECTL_VERSION",
Value: "",
},
}

if err := app.Run(os.Args); err != nil {
Expand All @@ -192,6 +201,12 @@ func run(c *cli.Context) error {
}
}

// Use custom kubectl version if provided
kubectlVersion := c.String("kubectl-version")
if kubectlVersion != "" {
kubectlCmd = fmt.Sprintf("%s.%s", kubectlCmdName, kubectlVersion)
}

// Parse variables and secrets
vars, err := parseVars(c)
if err != nil {
Expand Down Expand Up @@ -293,9 +308,36 @@ func checkParams(c *cli.Context) error {
return fmt.Errorf("Missing required param: cluster")
}

if err := validateKubectlVersion(c, extraKubectlVersions); err != nil {
return err
}

return nil
}

// validateKubectlVersion tests whether a given version is valid within the current environment
func validateKubectlVersion(c *cli.Context, availableVersions []string) error {
kubectlVersionParam := c.String("kubectl-version")
// using the default version
if kubectlVersionParam == "" {
return nil
}

// using a custom version but no extra versions are available
if len(availableVersions) == 0 {
return fmt.Errorf("Invalid param: kubectl-version was set to %s but no extra kubectl versions are available", kubectlVersionParam)
}

// using a custom version ...
// return nil if included in available extra versions; error otherwise
for _, availableVersion := range availableVersions {
if kubectlVersionParam == availableVersion {
return nil
}
}
return fmt.Errorf("Invalid param kubectl-version: %s must be one of %s", kubectlVersionParam, strings.Join(availableVersions, ", "))
}

// getProjectFromToken gets project id from token
func getProjectFromToken(j string) string {
t := token{}
Expand Down
33 changes: 33 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,39 @@ func TestCheckParams(t *testing.T) {
assert.NoError(t, err)
}

func TestValidateKubectlVersion(t *testing.T) {
// kubectl-version is NOT set (using default kubectl version)
set := flag.NewFlagSet("default-kubectl-version", 0)
c := cli.NewContext(nil, set, nil)
availableVersions := []string{}
err := validateKubectlVersion(c, availableVersions)
assert.NoError(t, err, "expected validateKubectlVersion to return nil when no kubectl-version param was set")

// kubectl-version is set and extra kubectl versions are NOT available
set = flag.NewFlagSet("kubectl-version-set-no-extra-versions-available", 0)
c = cli.NewContext(nil, set, nil)
set.String("kubectl-version", "1.14", "")
availableVersions = []string{}
err = validateKubectlVersion(c, availableVersions)
assert.Error(t, err, "expected validateKubectlVersion to return an error when no extra kubectl versions are available")

// kubectl-version is set, extra kubectl versions are available, kubectl-version is included
set = flag.NewFlagSet("valid-kubectl-version", 0)
c = cli.NewContext(nil, set, nil)
set.String("kubectl-version", "1.13", "")
availableVersions = []string{"1.11", "1.12", "1.13", "1.14"}
err = validateKubectlVersion(c, availableVersions)
assert.NoError(t, err, "expected validateKubectlVersion to return an error when kubectl-version is set, extra kubectl versions are available, kubectl-version is included")

// kubectl-version is set, extra kubectl versions are available, kubectl-version is NOT included
set = flag.NewFlagSet("invalid-kubectl-version", 0)
c = cli.NewContext(nil, set, nil)
set.String("kubectl-version", "9.99", "")
availableVersions = []string{"1.11", "1.12", "1.13", "1.14"}
err = validateKubectlVersion(c, availableVersions)
assert.Error(t, err, "expected validateKubectlVersion to return nil when kubectl-version is set, extra kubectl versions are available, kubectl-version is NOT included")
}

func TestGetProjectFromToken(t *testing.T) {
token := "{\"project_id\":\"test-project\"}"
assert.Equal(t, "test-project", getProjectFromToken(token))
Expand Down

0 comments on commit 01d7120

Please sign in to comment.