Skip to content

Commit

Permalink
Adding THE KUBERNETES MIXIN (#228)
Browse files Browse the repository at this point in the history
This PR adds a Kubernetes mixin. It updates the design doc a little to reflect a couple of
param changes across the three verbs and harmonizes things using camelCase naming per the
[style guide](https://google.github.io/styleguide/jsoncstyleguide.xml?showone=Property_Name_Format#Property_Name_Format) from
the GOOG.

Closes #82
  • Loading branch information
jeremyrickard authored and carolynvs-msft committed Mar 19, 2019
1 parent 1ca67de commit 1bf86a1
Show file tree
Hide file tree
Showing 30 changed files with 1,869 additions and 17 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ build: build-client build-runtime azure helm
build-runtime: generate
$(MAKE) $(MAKE_OPTS) build-runtime MIXIN=porter -f mixin.mk BINDIR=bin
$(MAKE) $(MAKE_OPTS) build-runtime MIXIN=exec -f mixin.mk
$(MAKE) $(MAKE_OPTS) build-runtime MIXIN=kubernetes -f mixin.mk

build-client: generate
$(MAKE) $(MAKE_OPTS) build-client MIXIN=porter -f mixin.mk BINDIR=bin
$(MAKE) $(MAKE_OPTS) build-client MIXIN=exec -f mixin.mk
$(MAKE) $(MAKE_OPTS) build-client MIXIN=kubernetes -f mixin.mk

generate: packr2
go generate ./...
Expand All @@ -51,14 +53,17 @@ endif
xbuild-all:
$(MAKE) $(MAKE_OPTS) xbuild-all MIXIN=porter -f mixin.mk BINDIR=bin
$(MAKE) $(MAKE_OPTS) xbuild-all MIXIN=exec -f mixin.mk
$(MAKE) $(MAKE_OPTS) xbuild-all MIXIN=kubernetes -f mixin.mk

xbuild-runtime:
$(MAKE) $(MAKE_OPTS) xbuild-runtime MIXIN=porter -f mixin.mk BINDIR=bin
$(MAKE) $(MAKE_OPTS) xbuild-runtime MIXIN=exec -f mixin.mk
$(MAKE) $(MAKE_OPTS) xbuild-runtime MIXIN=kubernetes -f mixin.mk

xbuild-client:
$(MAKE) $(MAKE_OPTS) xbuild-client MIXIN=porter -f mixin.mk BINDIR=bin
$(MAKE) $(MAKE_OPTS) xbuild-client MIXIN=exec -f mixin.mk
$(MAKE) $(MAKE_OPTS) xbuild-client MIXIN=kubernetes -f mixin.mk

bin/mixins/helm/helm:
mkdir -p bin/mixins/helm
Expand Down Expand Up @@ -135,6 +140,7 @@ prep-install-scripts:

publish: prep-install-scripts
$(MAKE) $(MAKE_OPTS) publish MIXIN=exec -f mixin.mk
$(MAKE) $(MAKE_OPTS) publish MIXIN=kubernetes -f mixin.mk
# AZURE_STORAGE_CONNECTION_STRING will be used for auth in the following commands
if [[ "$(PERMALINK)" == "latest" ]]; then \
az storage blob upload-batch -d porter/$(VERSION) -s bin/$(VERSION); \
Expand Down
17 changes: 17 additions & 0 deletions cmd/kubernetes/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"github.com/deislabs/porter/pkg/kubernetes"
"github.com/spf13/cobra"
)

func buildBuildCommand(mixin *kubernetes.Mixin) *cobra.Command {
cmd := &cobra.Command{
Use: "build",
Short: "Generate Dockerfile contribution for invocation image",
RunE: func(cmd *cobra.Command, args []string) error {
return mixin.Build()
},
}
return cmd
}
16 changes: 16 additions & 0 deletions cmd/kubernetes/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import (
"github.com/deislabs/porter/pkg/kubernetes"
"github.com/spf13/cobra"
)

func buildInstallCommand(mixin *kubernetes.Mixin) *cobra.Command {
return &cobra.Command{
Use: "install",
Short: "Use kubectl to apply manifests to a cluster",
RunE: func(cmd *cobra.Command, args []string) error {
return mixin.Install()
},
}
}
41 changes: 41 additions & 0 deletions cmd/kubernetes/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"fmt"
"io"
"os"

"github.com/deislabs/porter/pkg/kubernetes"
"github.com/spf13/cobra"
)

func main() {
cmd := buildRootCommand(os.Stdin)
if err := cmd.Execute(); err != nil {
fmt.Printf("err: %s\n", err)
os.Exit(1)
}
}

func buildRootCommand(in io.Reader) *cobra.Command {
mixin := kubernetes.New()
mixin.In = in
cmd := &cobra.Command{
Use: "kubernetes",
Long: "kuberetes is a porter 👩🏽‍✈️ mixin that you can you can use to apply kubernetes manifests in your bundle",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
mixin.Out = cmd.OutOrStdout()
mixin.Err = cmd.OutOrStderr()
},
SilenceUsage: true,
}

cmd.PersistentFlags().BoolVar(&mixin.Debug, "debug", false, "Enable debug logging")
cmd.AddCommand(buildVersionCommand(mixin))
cmd.AddCommand(buildBuildCommand(mixin))
cmd.AddCommand(buildInstallCommand(mixin))
cmd.AddCommand(buildUpgradeCommand(mixin))
cmd.AddCommand(buildUnInstallCommand(mixin))
cmd.AddCommand(buildSchemaCommand(mixin))
return cmd
}
17 changes: 17 additions & 0 deletions cmd/kubernetes/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"github.com/deislabs/porter/pkg/kubernetes"
"github.com/spf13/cobra"
)

func buildSchemaCommand(mixin *kubernetes.Mixin) *cobra.Command {
cmd := &cobra.Command{
Use: "schema",
Short: "Print the json schema for the mixin",
RunE: func(cmd *cobra.Command, args []string) error {
return mixin.PrintSchema()
},
}
return cmd
}
16 changes: 16 additions & 0 deletions cmd/kubernetes/uninstall.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import (
"github.com/deislabs/porter/pkg/kubernetes"
"github.com/spf13/cobra"
)

func buildUnInstallCommand(mixin *kubernetes.Mixin) *cobra.Command {
return &cobra.Command{
Use: "uninstall",
Short: "Use kubectl to delete resources contained in a manifest from a cluster",
RunE: func(cmd *cobra.Command, args []string) error {
return mixin.Uninstall()
},
}
}
16 changes: 16 additions & 0 deletions cmd/kubernetes/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import (
"github.com/deislabs/porter/pkg/kubernetes"
"github.com/spf13/cobra"
)

func buildUpgradeCommand(mixin *kubernetes.Mixin) *cobra.Command {
return &cobra.Command{
Use: "Upgrade",
Short: "Use kubectl to apply manifests to a cluster",
RunE: func(cmd *cobra.Command, args []string) error {
return mixin.Upgrade()
},
}
}
16 changes: 16 additions & 0 deletions cmd/kubernetes/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import (
"github.com/deislabs/porter/pkg/kubernetes"
"github.com/spf13/cobra"
)

func buildVersionCommand(mixin *kubernetes.Mixin) *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Print the mixin verison",
Run: func(cmd *cobra.Command, args []string) {
mixin.PrintVersion()
},
}
}
30 changes: 13 additions & 17 deletions docs/content/design/kubernetes-mixin.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,8 @@ The mixin allows bundle authors to specify the following parameters on install:
|-----------|------|-------------|---------|
| `namespace` | string | The namespace in which to create resources | `default` |
| `manifests` | string | The path to the manifests. Can be a file or directory | `/cnab/app/kubernetes` |
| `allow-missing-template-keys` | boolean | If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. | `true` |
| `output` | string | Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file. | |
| `record` | boolean | Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists. | `false` |
| `save-config` | boolean | If true, the configuration of current object will be saved in its annotation. Otherwise, the annotation will be unchanged. This flag is useful when you want to perform kubectl apply on this object in the future. | `false` |
| `record` | boolean | Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists. | `false` |
| `selector` | string | Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2) | |
| `template` | string | Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates | |
| `validate` | boolean | If true, use a schema to validate the input before sending it | `true` |
| `wait` | boolean | If true, wait for resources to be gone before returning. This waits for finalizers. | `true` |

Expand All @@ -67,15 +63,12 @@ The mixin allows bundle authors to specify the following parameters on install:
|-----------|------|-------------|---------|
| `namespace` | string | The namespace in which to create resources. | `default` |
| `manifests` | string | The path to the manifests. Can be a file or directory. | `/cnab/app/kubernetes` |
| `allow-missing-template-keys` | boolean | If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. | `true` |
| `force` | boolean | If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation. Overrides `grace-period`. | `false`|
| `grace-period` | integer | Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. If `force` is true, will result in 0. | -1 |
| `output` | string | Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file. | |
| `force` | boolean | If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation. Overrides `gracePeriod`. | `false`|
| `gracePeriod` | integer | Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. If `force` is true, will result in 0. | -1 |
| `overwrite` | boolean | Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration. | `true` |
| `record` | boolean | Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists. | `false` |
| `save-config` | boolean | If true, the configuration of current object will be saved in its annotation. Otherwise, the annotation will be unchanged. This flag is useful when you want to perform kubectl apply on this object in the future. | `false` |
| `prune` | boolean | Automatically delete resource objects, including the uninitialized ones, that do not appear in the configs. | `false` |
| `record` | boolean | Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists. | `false` ||
| `selector` | string | Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). | |
| `template` | string | Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates. | |
| `timeout` | integer | The length of time (in seconds) to wait before giving up on a delete, zero means determine a timeout from the size of the object. | 0 |
| `validate` | boolean | If true, use a schema to validate the input before sending it. | `true` |
| `wait` | boolean | If true, wait for resources to be gone before returning. This waits for finalizers. | `true` |
Expand All @@ -93,13 +86,15 @@ The mixin allows bundle authors to specify the following parameters on delete:
| `namespace` | string | The namespace in which to create resources. | `default` |
| `manifests` | string | The path to the manifests. Can be a file or directory. | `/cnab/app/kuberentes` |
| `force` | boolean | If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation. Sets grace period to `0`. | `false` |
| `grace-period` | integer | Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. | `-1` |
| `gracePeriod` | integer | Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. | `-1` |
| `timeout` | integer | The length of time (in seconds) to wait before giving up on a delete, zero means determine a timeout from the size of the object. | 0 |
| `wait` | boolean | If true, wait for resources to be gone before returning. This waits for finalizers. | `true` |

### Outputs

This mixin will leverage the `kubectl get` command in order to populate outputs. Given the wide range of objects that can be created, the mixin will support JSON Path to specify how to retrieve values to populate outputs. Bundle authors will specify the object type, name and provide a JSONPath to obtain the data. For example, to obtain the ClusterIP of a a given service, consider the following porter.yaml excerpt:
This mixin will leverage the `kubectl get` command in order to populate outputs. Given the wide range of objects that can be created, the mixin will support JSON Path to specify how to retrieve values to populate outputs. Bundle authors will specify the object type, name and provide a JSONPath to obtain the data. The mixin will not attempt further processing of the data, so if a JSONPath expression is given that results in multiple items, the JSON representing that will be stuck into the output as is. Namespace will default to `default` if not specified

For example, to obtain the ClusterIP of a a given service, consider the following porter.yaml excerpt:

```yaml
install:
Expand All @@ -108,7 +103,8 @@ install:
manifests: "/cnab/app/manifests/super-cool-app"
outputs:
- name: cluster_ip
resource_type: "service"
resource_name: "super-cool-service"
jsonpath: "spec.clusterIP"
resourceType: "service"
resourceName: "super-cool-service"
namespace: "cool"
jsonPath: "spec.clusterIP"
```
19 changes: 19 additions & 0 deletions pkg/kubernetes/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package kubernetes

import (
"fmt"
)

const kubeVersion = "v1.13.0"
const dockerFileContents = `RUN apt-get update && \
apt-get install -y apt-transport-https curl && \
curl -o kubectl https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/amd64/kubectl && \
mv kubectl /usr/local/bin && \
chmod a+x /usr/local/bin/kubectl
`

// Build generates the relevant Dockerfile output for this mixin
func (m *Mixin) Build() error {
_, err := fmt.Fprintf(m.Out, dockerFileContents, kubeVersion)
return err
}
22 changes: 22 additions & 0 deletions pkg/kubernetes/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package kubernetes

import (
"testing"

"github.com/deislabs/porter/pkg/context"
)

type TestMixin struct {
*Mixin
TestContext *context.TestContext
}

func NewTestMixin(t *testing.T) *TestMixin {
c := context.NewTestContext(t)
m := New()
m.Context = c.Context
return &TestMixin{
Mixin: m,
TestContext: c,
}
}
Loading

0 comments on commit 1bf86a1

Please sign in to comment.