Giter Club home page Giter Club logo

k8s-cluster-bundle's Introduction

Cluster Bundle

GoDoc

Note: This is not an officially supported Google product

The Cluster Bundle is a Kubernetes-related project developed to improve the way we package and release Kubernetes software. It was created by the Google Kubernetes team, and was developed based on our experience managing Kubernetes clusters and applications at scale in both GKE and GKE On-Prem.

It is currently experimental (Pre-Alpha) and will have frequent, breaking changes until the API settles down.

Installation

Most users will interact with Cluster Bundle objects via the command line interface bundlectl.

To install, run:

go install github.com/GoogleCloudPlatform/k8s-cluster-bundle/cmd/bundlectl

Packaging in the Cluster Bundle

Packaging in Cluster Bundle revolves around a new type, called the Component:

  • Component: A versioned collection of Kubernetes objects. A component should correspond to a logical application. For example, we might imagine components for each of Etcd, Istio, KubeProxy, and CoreDNS.
  • ComponentBuilder: An intermediate type that allows for easier creation of component objects.

The Cluster Bundle APIs are minimal and focused on the problem of packaging Kubernetes objects into a single unit. It is assumed that some external deployment mechanisms like a command line interface or an in-cluster controller will consume the components and apply the Kubernetes objects in the component to a cluster.

Components

Here's how you might use the Component type to represent Etcd:

apiVersion: bundle.gke.io/v1alpha1
kind: Component
spec:

  # A human readable name for the component.
  componentName: etcd-component

  # SemVer version for the component representing any and all changes to the
  # component or included object manifests. This should not be tied to the
  # application version.
  version: 30.0.2

  # A list of included Kubernetes objects.
  objects:
  - apiVersion: v1
    kind: Pod
    metadata:
      name: etcd-server
      namespace: kube-system
    spec:
      containers:
      - command:
        - /bin/sh
        - -c
        - |
          exec /usr/local/bin/etcd
    # ... and so on

Some notes about components:

  • Components have a name and version. The pair of these should be a unique identifier for the component.
  • Objects must be Kubernetes objects -- they must have apiVersion, kind, and metadata. Other than that, there are no restrictions on what objects can be contained in a component.

You can see more about examples of components in the examples directory.

Building

To make it easier to create components, you can use the ComponentBuilder type:

apiVersion: bundle.gke.io/v1alpha1
kind: ComponentBuilder

componentName: etcd-component

version: 30.0.2

# File-references to kubernetes objects that make up this component.
objectFiles:
- url: file:///etcd-server.yaml

ComponentBuilders can be built into Components with:

bundlectl build --input-file=my-component.yaml

During build, objects are inlined, which means they are imported directly into the component.

Raw-text can also be imported into a component. When inlined, this text is converted into a ConfigMap and then added to the objects list:

apiVersion: bundle.gke.io/v1alpha1
kind: Component
spec:
  canonicalName: data-blob
  version: 0.1.0
  rawTextFiles:
  - name: data-blob
    files:
    - url: file:///some-data.txt

Patching

The Cluster Bundle library provides a new type called the PatchTemplate to perform Kustomize-style patching to objects in component. These are Go-templates that are applied to the objects in a component via StrategicMergePatch or, if the object is not a Kubernetes Object, via JSONPatch.

For example, if you have the following PatchTemplate specified as an object in a component:

apiVersion: bundle.gke.io/v1alpha1
kind: PatchTemplate
template: |
  apiVersion: v1
  kind: Pod
  metadata:
    namespace: {{.namespace}}

and the following Kubernetes object in the same component:

apiVersion: v1
kind: Pod
metadata:
  name: etcd-server
spec:
  containers:
  - command:
    - /bin/sh
    - -c
    - |
      exec /usr/local/bin/etcd
  # etc...

and you specified following options YAML file:

namespace: foo-namespace

Then, running the bundlectl command like so:

bundlectl patch --input-file=etcd-component.yaml --options-file=options.yaml

produces:

apiVersion: v1
kind: Pod
metadata:
  name: etcd-server
  namespace: foo-namespace
spec:
  containers:
  - command:
    - /bin/sh
    - -c
    - |
      exec /usr/local/bin/etcd
  # etc...

For more examples of Patching and Patch-Building, see the examples directory.

Options Schema for PatchTemplates

Additionally, you can provide an OpenAPIv3 schema to validate and provide defaults for options applied to patch templates. Extending the example above, we can provide defaulting and validation for our namespace parameter.

apiVersion: bundle.gke.io/v1alpha1
kind: PatchTemplate
template: |
  apiVersion: v1
  kind: Pod
  metadata:
    namespace: {{.namespace}}
optionsSchema:
  properties:
    namespace:
      type: string
      pattern: '^[a-z0-9-]+$'
      default: dev-ns

Here, we require the namespace parameter to be a string that must match the regex pattern. Additionally, if the namespace parameter is not supplied, then we default namespace to dev-ns.

Filtering

bundlectl can filter objects from a component, which allows for powerful chaining of the bundlectl command.

# Filter all config map objects
bundlectl filter -f my-component.yaml --filterType=objects --kind=ConfigMap

# Keep only config map objects.
bundlectl filter -f my-component.yaml --filterType=objects --kind=ConfigMap --invert-match

Component Testing

The Cluster Bundle library provides experimental support for testing building, patching, and validating components, by writing YAML test-suites that are run via go test.

To write a component test suite, create a YAML test file like so:

componentFile: etcd-component-builder.yaml
rootDirectory: './'

testCases:
- description: Success
  apply:
    options:
      namespace: default-ns
      buildLabel: test-env
  expect:
    objects:

    # The kind + the name references a unique object in the component.
    - kind: Pod
      name: etcd-server

      # ensure that the generated 'etcd-server' object YAML has the following
      substrings
      findSubstrs:
      - 'build-label: test-env'
      - 'image: k8s.gcr.io/etcd:3.1.11'
      - 'namespace: default-ns'

      # ensure that the generated 'etcd-server' object YAML does not have the
      # following substrings
      notFindSubstrs:
      - 'build-label: dev-env'

- description: 'Fail: parameter missing'
  expect:
    # Check for an error condition during the apply-process.
    applyErrSubstr: 'namespace in body is required'

Then, you need only write the following go boilerplate:

package component

import (
	"testing"

	"github.com/GoogleCloudPlatform/k8s-cluster-bundle/pkg/testutil/componentsuite"
)

func TestComponentSuite(t *testing.T) {
	componentsuite.Run(t, "test-suite.yaml")
}

Running the tests can be performed by running go test.

Examples:

Public APIs and Compatibility.

During pre-Alpha and Alpha, breaking changes may happen in any of the types in pkg/apis, the bundlectl CLI, or the Go APIs (methods, structs, interfaces not in pkg/apis).

During Beta, backwards incompatible breaking changes in the bundlectl and the pkg/apis directory will only happy during Major version boundaries.

The Go APIs are not covered by any compatibility guarantee and can break in backwards incompatible ways during any release

Development

Directory Structure

This directory follows a layout similar to other Kubernetes projects.

  • pkg/: Library code.
  • pkg/apis: APIs and schema for the Cluster Bundle.
  • pkg/client: Generated client for the Cluster Bundle types.
  • config/crds: Generated CRDs for the ClusterBundle types.
  • examples/: Examples of components
  • cmd/: Binaries. This contains the bundlectl CLI tool.

Building and Testing

The Cluster Bundle relies on Bazel for building and testing, but the library is go-gettable and it works equally well to use go build and go test for building and testing.

Testing

To run the unit tests, run

bazel test //...

It should also work to use the go command:

go test ./...

Regenerate BUILD files

To make using Bazel easier, we use Gazelle to automatically write Build targets. To automatically write the Build targets, run:

bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro 'repositories.bzl%gomod_repositories'

Regenerate Generated Code

We rely heavily on Kubernetes code generation. To regenerate the code, run:

hack/update-codegen.sh

If new files are added, you will need to re-run Gazelle (see above).

Prow

We use prow to test the Cluster Bundle. If the prow-jobs need to be updated, see the Cluster Bundle Prow Jobs:

k8s-cluster-bundle's People

Contributors

ahmedtd avatar artasparks avatar avrittrohwer avatar bradfordmedeiros avatar esert-g avatar foolusion avatar garron avatar giuliano-sider avatar howardjohn avatar jiayingz avatar johnbelamaric avatar jonathansun avatar jplobosgle avatar justinsb avatar mikedanese avatar nicolexin avatar penghez avatar random-liu avatar trshafer avatar u5surf avatar wangzhen127 avatar zyqsempai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

k8s-cluster-bundle's Issues

RawExtension for arbitrary JSON

Kubernetes uses the RawExtension type to represent arbitrary JSON components. Right now in the proto-world, I'm using Struct and in the Go IDL version (soon to come) I'll probably use map[string]interface{} for compatibility. But this is non-standard and the standard type for this purpose is RawExtension.

https://github.com/kubernetes/apimachinery/blob/ed135c5b96450fd24e5e981c708114fbbd950697/pkg/runtime/types.go

type RawExtension struct {
	// Raw is the underlying serialization of this object.
	//
	// TODO: Determine how to detect ContentType and ContentEncoding of 'Raw' data.
	Raw []byte `protobuf:"bytes,1,opt,name=raw"`
	// Object can hold a representation of this extension - useful for working with versioned
	// structs.
	Object Object `json:"-"`
}

API: Status for Bundle and Components?

Right now the Bundle is just a spec. The components will also be converted to be in a spec too. But that opens tho question: What should be in the Status? It seems like there should be something

Testing: Round-Trip Converter Tests

We should add tests to converter library to ensure that round-trip conversion works and gives the same result (->ToObject->ToYAML->ToObject). It would help catch issues like #93

Add labels for RawText objects indicate source

Right now, raw text objects are inlined as ConfigMaps. This is a for using the existing K8S infrastructure, but it can be nice to know when the config maps are actually sourced from raw text.

Solution for this is to add labels or annotations indicating that it was a raw-text source.

Add a fluid style go api

Maybe this is just me, but I would really love a fluid/functional style API for the bundle library. Most of these transformations feel like map/filter/reduce/transform, so it seems like it could work well.

fluid.FluidBundle(bundle).
  Filter().
  Assert(fn).
  TransformImages().
  FindComponent(cname).
  Get() 
-> Returns component

Generate components / patches with the CLI

The cluster bundle CLI should be able to generate configuration (in a similar way to kubebuilder). This really reduces the friction when using the tooling and helps enforce design patterns.

Add validation for CRDs

We should add validation for CRDs. The Kubernetes libraries provide this functionality, but I held off on this due to import-dependency-issues.

Add validation for MinRequirements

Now that there's a MinRequirements, there should be some validation on a Bundle that the Bundle's components in the bundle meet a specific components' min requirements.

New name for Bundle CLI

Right now, I've been calling the Bundle CLI bundler. I think it would be good to have a better name. Some ideas:

  • bundlectl
  • cbundle
  • kbundle
  • kubebundle

Rename / Rethink ClusterObjectKey

The ClusterObjectKey serves as a reference to select a single Kubernetes object from the bundle. It doesn't quite fit. Here are some ideas:

  • Name it ClusterObjectRef
  • Reuse the ObjectRef object

Add remote file support

The bundle allows for arbitrary urls in the bundle. At the moment, the libarary only supports local files, but we should also support remote files (AWS, GCS, http, etc)

Rethink/Design: Options

This is a note to me that options probably needs more design work. Right now there's set of top-level options keys. I think each component should have their own options specified -- rather like helm has Values for every component. That being said, I think there probably needs to be 'global' options as well, which would have things like 'namespace' and 'clusterName'.

And if there is a top-level global options, is there a translation between that and the component-level options?

Filter API: change KeepOnly to RemoveOnly

A TODO for myself in the Filter API. I'd like to change KeepOnly to RemoveOnly, effectively inverting the logic. I think KeepOnly should be the default behavior -- you're more likely to select what you want rather than select what you don't want.

Rethink minRequirements design

For now, the min requirements and component API version will be removed. This sort of version dependency is hard and needs a more careful design.

Add an 'images' field to the Bundle

Feature request under consideration: perhaps we should add a list of images to the Bundle.

Reasoning:

  • Pause container probably won't be referenced directly by application manifests
  • Some manifests might themselves be hardcoded elsewhere.

So, for flexibility, having a list of all the images would be very useful. Especially for things like private registries.

Add object-lookup based on ObjectRef

Right now, find.Finder does object lookup based on the name. That's not ideal -- it should be using the ObjectRef (since name is not guaranteed to be unique).

API: Add raw object types.

Right now, the inlined type is really a parseable-as-yaml/json type. But this excludes templates that are specified as un-parseable go/jsonnet/salt (etc.) templates. I think the way to support this is to have a new url / inlined type called 'raw' or somesuch, that is just not parseable.

So right now there's

  repeated google.protobuf.Struct cluster_objects = 7;
  repeated File cluster_object_files = 8;

this would imply adding

  repeated string raw_objects = 7; // or maybe bytes
  repeated string raw_object_files = 8;

This would add a great deal of flexibility (it could support helm, for example).

Add validation for version strings

Now that versions have a strict meaning in the cluster bundle sense, there should be tooling to enforce version numbers are semantic version numbers.

API: Component spec

Right now, the components don't have a spec / status. This make them problematic to apply to the cluster and act on them. I think this means that they probably need to be full spec / status objects (even though that adds a bit of noise to the schema)

Bundle edit

One cool feature would be to add an edit command to the bundle to edit things like images, namespaces, and so on.

For example:

bundlectl edit images -f bundle.yam

Which would pop you over to an editor.

Of course, this would work best with local files, but might be possible with remote files

API: Add configurable validation

Right now the validation is baked in. The cluster bundle becomes even more compelling if there are variable levels of validation in both the Bundle and Component.

I'm imagining something like:

message Validation {
  bool disable_unique_metadata = 1:
  bool enable_image_validation = 2;
}

Or maybe it's just a string->bool flag mapping

message Validation {
  map<string, bool> options
}

Or maybe just a list of options

message Validation {
  repeated string = 1;
}

Both these latter options are more extensible. But that means that the validation phase needs to be configurable (which it probably should be, I suppose).

Inlining raw-text doesn't work for nested child components

Looks like inlining raw-text doesn't work for nested child components. Basically, just need to replicate the following for raw text also:

» » » for _, ocf := range comp.GetSpec().GetClusterObjectFiles() {
» » » » compUrl := cf.GetUrl()
» » » » objUrl := ocf.GetUrl()
» » » » if strings.HasPrefix(objUrl, "file://") && strings.HasPrefix(compUrl, "file://") {
» » » » » ocf.Url = "file://" + filepath.Join(filepath.Dir(shortFileUrl(compUrl)), shortFileUrl(objUrl))
» » » » }
» » » }

Add diffing functionality

It would be really cool to have diffing functionality for bundles, node configs, components, cluster objects. Not something that is planned for the very near term, but definitely something planned for reasonably soon -- it's essentially for building automation.

Add filtering for components

After using this library, I've found it would be very useful to be able to filter specific components, as I've done for bundles.

API: Move Patching, NodeConfig to Extensions

Here's my idea for unifying patching and node config, both of which have seemed pretty adhoc: Move them to an 'extensions' API and include them as just ordinary objects in a component.

pkg/apis/extensions/v1alpha1/ for example.

This has the advantage of greatly simplifying what a component package is. Moreover, these objects can reuse the searching and externalization infrastructure already existing in components.

Embedding tool

It would be very useful to have an embedding tool built into bundlectl or adjacent as a helper tool. This would be like a simpler, standalone version of the Bazel go_embed rule, which would take a file and embed it as a raw string or as bytes in a go map.

Add a true K8S API for the bundle

Currently the bundle and component types are specified as proto. That's fine for working with the Bundle, but for actually applying to the cluster we need a CRD. Also, for ease of use, a client library would be useful.

So, practically, this means making a go struct (aka go IDL) version of the api and running kubebuilder.

Add a config generator command

It would significantly improve the UX of using the bundle if, like with kubebuilder, there was a way to generate Bundle and Component configuration.

Maybe something like:

bundlectl generate --out_dir=...  -f existingbundlr.yaml

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.