Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding THE KUBERNETES MIXIN #228

Merged
merged 2 commits into from
Mar 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've also figured out that we want SilenceUsage: true, here as well. Otherwise it prints the help text anytime the an error happens.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question on the switch from snakes to camels. Is that something that should be done for all outputs? Or because of k8s? I'm not quite clear on why you made the switch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for consistency.

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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Thanks for picking up on this recent change.

m.Context = c.Context
return &TestMixin{
Mixin: m,
TestContext: c,
}
}
Loading