## File: README.md

# Cloud Development Kit for Kubernetes



[](https://github.com/cdk8s-team/cdk8s/actions/workflows/website.yml)

[](https://constructs.dev/packages/cdk8s)

**cdk8s** is an open-source software development framework for defining Kubernetes applications and reusable abstractions using familiar
programming languages and rich object-oriented APIs. cdk8s apps synthesize into standard Kubernetes
manifests which can be applied to any Kubernetes cluster.

cdk8s is a [Cloud Native Computing Foundation](https://www.cncf.io) Sandbox Project, built with ❤️ at AWS. We encourage you to [try it out](#getting-started), [leave feedback](#help--feedback), and [jump in to help](#contributing)!

Contents:

- [Repositories](#repositories)
- [Overview](#overview)
- [Getting Started](#getting-started)
- [Help \& Feedback](#help--feedback)
- [Documentation](#documentation)
- [Examples](#examples)
- [Roadmap](#roadmap)
- [Community](#community)
- [Contributing](#contributing)
- [CDK8s.io website](#cdk8sio-website)
- [License](#license)

## Repositories

This project consists of multiple packages, maintained and released via the following repositories:

- [cdk8s](https://github.com/cdk8s-team/cdk8s-core) - Core library. For historical reasons note that the [`cdk8s`](https://www.npmjs.com/package/cdk8s) package is maintained in the `cdk8s-team/cdk8s-core` repository.
- [cdk8s-cli](https://github.com/cdk8s-team/cdk8s-cli) - Command-Line interface.
- [cdk8s-plus](https://github.com/cdk8s-team/cdk8s-plus) - High-Level constructs for Kubernetes core.

The current repository acts as an umbrella repository for cross module concerns, as well as the deployment of [`cdk8s.io`](https://cdk8s.io)

## Overview

**cdk8s** apps are programs written in one of the supported programming
languages. They are structured as a tree of
[constructs](https://github.com/aws/constructs).

The root of the tree is an `App` construct. Within an app, users define any
number of charts (classes that extend the `Chart` class). Each chart is
synthesized into a separate Kubernetes manifest file. Charts are, in turn,
composed of any number of constructs, and eventually from resources, which
represent any Kubernetes resource, such as `Pod`, `Service`, `Deployment`,
`ReplicaSet`, etc.

cdk8s apps only ***define*** Kubernetes applications, they don't actually apply
them to the cluster. When an app is executed, it *synthesizes* all the charts
defined within the app into the `dist` directory, and then those charts can be
applied to any Kubernetes cluster using `kubectl apply -f dist/chart.k8s.yaml` or a GitOps tool like [Flux](https://fluxcd.io/).

> **cdk8s** is based on the design concepts and technologies behind the [AWS
Cloud Development Kit](https://aws.amazon.com/cdk), and can interoperate with
AWS CDK constructs to define cloud-native applications that include both
Kubernetes resources and other CDK constructs as first class citizens.

Read [our blog](https://aws.amazon.com/blogs/containers/introducing-cdk-for-kubernetes/) or [watch our CNCF webinar](https://www.cncf.io/webinars/end-yaml-engineering-with-cdk8s/) to learn more and see a live demo of cdk8s in action.

## Getting Started

See the [Getting Started](https://cdk8s.io/docs/latest/get-started) guide in
[cdk8s Documentation](https://cdk8s.io/docs/).

## Help & Feedback

Interacting with the community and the development team is a great way to
contribute to the project. Please consider the following venues (in order):

- Search [open issues](https://github.com/cdk8s-team/cdk8s/issues)
- Stack Overflow: [cdk8s](https://stackoverflow.com/questions/tagged/cdk8s)
- File a [new issue](https://github.com/cdk8s-team/cdk8s/issues/new/choose)
- Slack: #cdk8s channel in [cdk.dev](https://cdk.dev)

## Documentation

See [cdk8s Documentation](https://cdk8s.io/docs).

## Examples

See our [Examples Directory](./examples).

## Roadmap

See our [roadmap](https://github.com/cdk8s-team/cdk8s/projects/1) for details about our plans for the project.

## Community

See [Awesome cdk8s](https://github.com/dungahk/awesome-cdk8s).

If you're a cdk8s user please consider adding your name to the [ADOPTERS](./ADOPTERS.md) file.

## Contributing

The cdk8s project adheres to the [CNCF Code of
Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).

We welcome community contributions and pull requests. See our [contribution
guide](./CONTRIBUTING.md) for more information on how to report issues, set up a
development environment and submit code.

## CDK8s.io website

See [Docs Directory](./docs/README.md).

## License

This project is distributed under the [Apache License, Version 2.0](./LICENSE).

---

## File: docs/basics/api-object.md

# ApiObject

An `ApiObject` is a construct that represents an entry in a Kubernetes manifest (level 0).
In most cases, you won't use `ApiObject` directly but rather use classes that
are imported through `cdk8s import` and which extend this base class.

By default, when you import API objects from k8s, all class names will all be
prefixed with `Kube` to differentiate them from the high-level APIs in cdk8s+.
It is possible to customize the prefix by providing a string value to the
`--class-prefix` flag, or by passing `--no-class-prefix`. (Note: if no prefix is
used for k8s resources, this may lead to conflicts - see
https://github.com/cdk8s-team/cdk8s/issues/140).

### Resource Names

By default, cdk8s will autogenerate a name for every resource when it is instantiated.
The name is created from the path of the construct, and suffixed with a hash of that path.

For example, the following code:

```ts
import { App, Chart } from 'cdk8s';
import * as kplus from 'cdk8s-plus-34';

const app = new App();
const chart = new MyChart(app, 'my-chart');

new kplus.Deployment(chart, 'deployment', {
  containers: [{ image: 'nginx' }]
});

app.synth();
```

generates a `Deployment` with the name: `my-chart-deployment-c8c354dd`. Where `my-chart` is the id of the `Chart`, `deployment` is the id of the `Deployment`, and `c8c354dd` is the hash suffix.

You can programmatically access the name of any resource in a cdk8s app by accessing the `.name` property of a resource. For example:

```ts
const deployment = new kplus.Deployment(chart, 'deployment', {
  containers: [{ image: 'nginx' }]
});
const deploymentName = deployment.name;
```

To remove hashes from resource names, you can set the `disableResourceNameHashes` property to `true`:

```ts
...
const chart = new MyChart(app, 'my-chart', {
    disableResourceNameHashes: true,
});
...
```
Which would similarly generate a `Deployment` with the name: `my-chart-deployment`. Note that disabling hashes introduces the possibility of name clashes. For example, the following code:

```ts
// think of this as a custom construct
const c = new Construct(this, 'Construct');

new kplus.Deployment(c, 'Deployment', {
  containers: [{ image: 'image' }]
});

new kplus.Deployment(this, 'Construct-Deployment', {
  containers: [{ image: 'image' }]
})
```

will produce two deployments with the same name: `chart-construct-deployment`.
This happens because cdk8s uses a hyphen (`-`) as the delimiter between elements
in the path, so if the id of your construct also contains a hyphen (`-`), a collision
can occur. In addition, you can explicitly set a name for every resource by using the `metadata.name` property:

```ts
new kplus.Deployment(c, 'Deployment', {
  metadata: {
    name: 'my-deployment'
  },
  ...
});
```

---

## File: docs/basics/app.md

# App

The `App` is the entry-point of your `cdk8s` application, and is the root of the constructs tree.
Every application first starts by creating an instance of an `App`.

```ts
import { App } from 'cdk8s';

const app = new App();
```

Then, [Charts](./chart.md) are registered into the app:

```ts
new MyChart(app, 'Chart');
```

And finally, the `app` is synthesized in order to produce the YAML manifests. Overall, your `cdk8s` 
application will have the following high level structure to it:

```ts
import { App } from 'cdk8s';

// create the app entry-point
const app = new App();

// register charts to the app
new MyChart(app, 'Chart');

// synthesize the app
app.synth();
```

## Resolvers

Resolvers are a mechanism to inject custom logic into the cdk8s value resolution process. 
It allows to transform any value just before being written to the Kubernetes manifest. 

For example, lets create a resolver that adds a prefix to every resource in our application.
To define a custom resolver, create a class that implements the `IResolver` interface:

```ts
import { IResolver, ResolutionContext } from 'cdk8s';

export class AddNamePrefixResolver implements IResolver {

  constructor(private readonly prefix: string) {}

  public resolve(context: ResolutionContext): void {

    // check if we are resolving the name property
    const isNameProperty = context.key.includes('metadata') && context.key.includes('name') && context.key.length === 2;

    // check we haven't already added a prefix. this is important because
    // resolution is recursive, so we need to avoid infinite recursion.
    const isPrefixed = typeof(context.value) === 'string' && context.value.startsWith(this.prefix);

    if (isNameProperty && !isPrefixed) {
      // replace the value with a new one
      context.replaceValue(`${this.prefix}${context.value}`);
    }
  }

}
```

The `context` argument contains information about the value that is currently being resolved:

- **obj**: `ApiObject` currently being resolved.
- **key**: Array containing the JSON path elements of the keys leading up to the value.
- **value**: The original value.

It also contains the `replaceValue` method you should use to set a replacement value instead of the original.
When you create a cdk8s `App`, pass the resolver instance to it via the `resolvers` property:

```ts
import { App, Chart } from 'cdk8s'

const app = new App({ resolvers: [new AddNamePrefixResolver('custom-prefix')] });
new Chart(app, 'Chart');
```

When you run `cdk8s synth`, your custom logic will be invoked, allowing you to replace the 
original value. 

> When passing multiple resolvers, the first one (by natural order of the array) that invokes `replaceValue` is considered, and others are ignored.

One common use-case for resolvers is to fetch values from deployed infrastructure. 
This allows authoring cdk8s applications that natively leverage managed cloud resources. 
To that end, two specific resolvers are available that allow you to integrate cdk8s with other CDK frameworks:

- [AWS CDK Resolver](https://github.com/cdk8s-team/cdk8s-awscdk-resolver)
- [CDK For Terraform Resolver](https://github.com/cdk8s-team/cdk8s-cdktf-resolver)

---

## File: docs/basics/chart.md

# Chart

The `Chart` is a container that synthesizes a single Kubernetes manifest.

```ts
class MyChart extends Chart {
  constructor(scope: Construct, ns: string) {
    super(scope, ns);

    // add contents here
  }
}
```

During synthesis, charts collect all the `ApiObject` nodes (recursively) and
emit a single YAML manifest that includes all these objects.

When a chart is defined, you can specify chart-level `namespace` and `labels`.
Those will be applied to all API objects defined within the chart (recursively):

```ts
class MyChart extends Chart {
  constructor(scope: Construct, ns: string) {
    super(scope, ns, {
      namespace: 'my-namespace',
      labels: {
        app: 'my-app',
      },
    });

    new ApiObject(this, 'my-object', {
      apiVersion: 'v1',
      kind: 'Foo'
    });
  }
}
```

Will synthesize into:

```yaml
apiVersion: v1
kind: Foo
metadata:
  namespace: my-namespace
  labels:
    app: my-app
```

---

## File: docs/basics/constructs.md

# Constructs

Constructs are the basic building block of **cdk8s**. They are the instrument
that enables composition and creation of higher-level abstractions through
normal object-oriented classes.

If you come from the Kubernetes world, you can think of constructs as
programmatically defined Helm Charts. The nice thing about constructs being
"programmatically defined" is that we can use them to leverage the full power of
object-oriented programming. For example:

* You can express the abstraction's API using strong-typed data types
* You can express rich interactions with methods and properties
* You can create polymorphic programming models through interfaces and base
  classes
* Share them through regular package managers
* Test them using our familiar testing tools and techniques
* Version them
* ...and do all that stuff that we've been doing with software in the past 20
  years.

---

## File: docs/basics/deps.md

# Dependencies

You can declare dependencies between any two cdk8s constructs using the `addDependency()` method.

### ApiObjects

For example, you can force kubernetes to first apply a `Namespace` before applying the `Service` in the scope of that namespace:

```typescript

const namespace = new k8s.KubeNamespace(chart, 'backend');
const service = new k8s.KubeService(chart, 'Service', { metadata: { namespace: namespace.name }});

// declare the dependency. this is just a syntactic sugar for Node.of(service).addDependency(namespace)
service.addDependency(namespace);
```

`cdk8s` will ensure that the `Namespace` object is placed before the `Service` object in the resulting manifest:

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: chart-backend-a59d2e47
---
apiVersion: v1
kind: Service
metadata:
  name: chart-service-93d02be7
  namespace: chart-backend-a59d2e47
```

### Charts

You can also specify dependencies between charts, in exactly the same manner. For example, if we have a chart that provisions our `namespace`, we need that chart to be applied first:

```typescript
const namespaceChart = new Chart(app, 'namespace');
const applicationChart = new Chart(app, 'application');

// declare the dependency. this is just a syntactic sugar for Node.of(applicationChart).addDependency(namespaceChart)
applicationChart.addDependency(namespaceChart);
```

Running `cdk8s synth` will produce the following `dist` directory:

```console
> cdk8s synth

dist/0000-namespace.k8s.yaml
dist/0001-application.k8s.yaml
```

Notice that the `namespace` chart appears first with the `0000` prefix. This will ensure that a subsequent execution of `kubectl apply -f dist/` will apply the `namespace` first, and the `application` second.

### Custom Constructs

The behavior above applies in the same way to custom constructs that you create or use.

```typescript
class Database extends Construct {
  constructor(scope: Construct, name: string) {
    super(scope, name);

    new k8s.KubeStatefulSet(this, 'StatefulSet');
    new k8s.KubeConfigMap(this, 'ConfigMap');
  }
}

const app = new App();

const chart = new Chart(app, 'Chart');

const service = new k8s.KubeService(chart, 'Service')
const database = new Database(chart, 'Database');

service.addDependency(database);
```

Declaring such a dependency will cause **each** `ApiObject` in the source construct, to *depend on* **every** `ApiObject` in the target construct.

Note that in the example above, the source construct is actually an `ApiObject`, which is also ok since it is essentially a construct with a single `ApiObject`.

> Note that if the source of your dependency is a custom construct, it won't have the `addDependency` syntactic sugar by default, so you'll have to use `Node.of()`.

The resulting manifest will be:

```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: chart-database-statefulset-4627f8e2
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: chart-database-configmap-676f8640
---
apiVersion: v1
kind: Service
metadata:
  name: chart-service-93d02be7
```

You can see that all `ApiObject`s of the `Database` construct, appear before the `Service` object.

### Things just got cool

If you simply declare a dependency between two `ApiObject`s (or `Constructs`), that belong to two different `Chart`s, `cdk8s` will create the chart dependency automatically for you.

```typescript
const namespaceChart = new Chart(app, 'namespace');
const applicationChart = new Chart(app, 'application');

const namespace = new k8s.KubeNamespace(namespaceChart, 'namespace');
const deployment = new k8s.KubeDeployment(applicationChart, 'Deployment');

// dependency between ApiObjects, not Charts!
deployment.addDependency(namespace);
```

Running `cdk8s synth` will produce the same result as if explicit chart dependencies were declared:

```console
> cdk8s synth

dist/0000-namespace.k8s.yaml
dist/0001-application.k8s.yaml
```

This means you need not be bothered with managing chart dependencies, simply work with the `ApiObject`s you create, and let `cdk8s` infer the chart dependencies.

---

## File: docs/basics/escape-hatches.md

# Escape Hatches

An "escape hatch" is an intentional _leak_ in the abstraction layer. It allows
users to "escape the abstraction" and reach out to a lower layer.

Similarly, in CDKs, escape hatches are mechanisms that allow users to tweak the
synthesized output when the abstraction they use does not "hold water".

You may need to use an escape hatch in the following cases:

1. You are using an imported API object (e.g. `KubeDeployment`) and there is an
   issue with the schema or a bug in "import" which results in an invalid
   manifest or missing fields (as an example see
   [issue #140](https://github.com/cdk8s-team/cdk8s/issues/140)).
2. You are using a high-level API (e.g. cdk8s+) which does not expose some
   functionality which exists in the lower-level resources.

## Patching API objects directly

The [`ApiObject`](./api-object.md) class, which is the base of all objects
synthesized into a Kubernetes manifest offers an API for patching the
synthesized output at the wire level using JSON Patch
([RFC-6902](http://tools.ietf.org/html/rfc6902)):

```ts
import { JsonPatch } from 'cdk8s';
apiObject.addJsonPatch(JsonPatch.replace('/foo', 'bar'));
apiObject.addJsonPatch(JsonPatch.add('/foo/bar/0', { bar: 123 }));
```

During synthesis, patches are applied in-order after the API object synthesized
itself.

All classes generated using the CLI [import](../cli/import.md) command extend
`ApiObject`, and therefore include the `addJsonPatch()` method.

## Patching API objects behind higher-level APIs

The second use case for using escape hatches is when you are working against a
higher-level construct which, for some reason, does not allow you to achieve
what you need.

For example, let's say you are using the `Pod` class from cdk8s+ and you wish to
set [`enableServiceLinks`] to `true`. This feature is currently not supported in
the cdk8s+ `Pod` API, so you'll want to patch the underlying `KubePod` and set
this value.

[`enableServiceLinks`]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core

To do that, you will need to "peak" into the construct tree and find the underlying
API object, so you can apply the patch to it:

```ts
import { Pod } from 'cdk8s-plus-34';
import { ApiObject } from 'cdk8s';

const pod = new Pod(...);
const kubePod = ApiObject.of(pod);
kubePod.addJsonPatch(...);
```

The `ApiObject.of()` method uses capabilities of the [constructs programming
model](./constructs.md) to find the "default child" of a construct
(`Node.of(c).defaultChild`). When a construct is initialized, it can either
explicitly assign the value of `Node.of(this).defaultChild = xxx` or it can use
the identity `Default` for one of its child constructs. This will automatically
identify it as the default child.

!!! tip
    The `ApiObject.of()` method recursively searches down the construct tree
    through child constructs called `Default` until it finds a child of type
    `ApiObject`. This means, for example, that `ApiObject.of(apiObject)` returns
    the same object.

There could be situations where a default child is not recorded by a high-level
construct. This still does not mean you are blocked from patching the underlying
API objects. You can still use `Node.of(x)` to traverse the construct tree to
obtain the child. For example, you can use `Node.of(x).findChild(id)` to
retrieve any child by its ID.

---

## File: docs/basics/helm.md

# Helm Support

You can use the `Helm` construct in order to include [Helm](https://helm.sh)
charts.

!!! note
    You will need helm to be [installed](https://helm.sh/docs/intro/install/) locally for this feature.
    For accessing private helm repositories, you must be authenticated to the repository in a way that the `helm pull` command recognizes.

The following example adds the
[bitnami/redis](https://github.com/bitnami/charts/tree/master/bitnami/redis)
Helm chart with sentinel containers enabled:

> The Bitnami helm repo needs to be added through: `helm repo add bitnami https://charts.bitnami.com/bitnami`

> For accessing private helm repositories, you must be authenticated to the repository in a way that the `helm template` 
> command recognizes.

```ts
class MyChart extends cdk8s.Chart {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const redis = new Helm(this, 'redis', {
      chart: 'bitnami/redis',
      values: {
        sentinel: {
          enabled: true
        }
      }
    });
  }
}
```

The `Helm` construct will render the manifest from the specified chart by
executing `helm template`. If `values` is specified, these values will override
the default values included with the chart.

The `name` option can be used to specify the chart's [release name](https://helm.sh/docs/intro/using_helm/#three-big-concepts).
If not specified, a valid and unique release name will be allocated
based on the construct path.

The `Helm` construct extends `Include` and inherits its API. For example, you
can use the `apiObjects` property to find and interact with included API
objects.

The following example shows how to add an annotation to the Redis master
deployment:

```ts
const master = redis.apiObjects.find(o => o.name === 'foo-redis-master');
master.metadata.addAnnotation('my.annotation', 'hey-there');
```

---

## File: docs/basics/include.md

# Include

The `Include` construct can be used to include an existing manifest in a chart.

The following example will include the Kubernetes Dashboard in `MyChart`:

```ts
import { Include } from 'cdk8s';

class MyChart extends Chart {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const dashboard = new Include(this, 'dashboard', {
      url: 'https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml',
      // or
      url: 'dashboard.yaml'
    });

    // ...other resources
  }
}
```

All API objects defined in the included manifest will be added as children
`ApiObject`s under the `Include` construct's scope and can be accessed
through the `apiObject` property:

The following example queries for all the `Deployment` resources in the
dashboard:

```ts
const deploymentApiObject = dashboard.apiObjects.find(c => c.kind === 'Deployment');
```

NOTE: names of included objects (`metadata.name`) are preserved. This means that
if you try to include the same manifest twice into the same chart, your manifest
will have duplicate definitions of the same objects.

---

## File: docs/basics/index.md

# Basic concepts of cdk8s

This section discusses core concepts in cdk8s.

!!! info

    The documentation here relates to version `2.x` of the cdk8s toolchain, which is the latest. If you are still using version `1.x`, please refer to the [Migrating from 1.x Guide](../migrating-from-1.x.md).