"""
# cdk8s

> Cloud Development Kit for Kubernetes

**cdk8s** is a software development framework for defining Kubernetes
applications using rich object-oriented APIs. It allows developers to leverage
the full power of software in order to define abstract components called
"constructs" which compose Kubernetes resources or other constructs into
higher-level abstractions.

This library is the foundation of **cdk8s**. It includes base types that are
used to define cdk8s applications.

## Chart

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

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
class MyChart(Chart):
    def __init__(self, scope, ns):
        super().__init__(scope, ns)
```

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

## ApiObject

An `ApiObject` is a construct that represents an entry in a Kubernetes manifest.
In most cases, you won't use `ApiObject` directly but rather use classes that
are generated by the cdk8s CLI and extend this base class.

## 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`:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
from cdk8s import Include

class MyChart(Chart):
    def __init__(self, scope, id):
        super().__init__(scope, id)

        dashboard = Include(self, "dashboard", {
            "url": "https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml",
            # or
            "url": "dashboard.yaml"
        })
```

All API objects defined in the included manifest will be added as children
`ApiObject`s under the `Include` construct's scope. This implies that you can
use `Node.of(include).children` to inspect them.

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

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
deps = Node.of(dashboard).children.filter((c: ApiObject) => 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.

### Dependencies

You can declare dependencies between various `cdk8s` constructs by using the built-in support of the underlying `constructs` model.

#### ApiObjects

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

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
namespace = k8s.Namespace(chart, "backend")
service = k8s.Service(chart, "Service", metadata={"namespace": namespace.name})

# declare the dependency. this is just a syntactic sugar for Node.of(service).addDependency(namespace)
service.add_dependency(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:

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
namespace_chart = NamespaceChart(app, "namespace")
application_chart = ApplicationChart(app, "application")

# declare the dependency. this is just a syntactic sugar for Node.of(applicationChart).addDependency(namespaceChart)
application_chart.add_dependency(namespace_chart)
```

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.

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
class Database(Construct):
    def __init__(self, scope, name):
        super().__init__(scope, name)

        k8s.StatefulSet(self, "StatefulSet")
        k8s.ConfigMap(self, "ConfigMap")

app = App()

chart = Chart(app, "Chart")

service = k8s.Service(chart, "Service")
database = Database(chart, "Database")

service.add_dependency(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.

```python
# Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
namespace_chart = NamespaceChart(app, "namespace")
application_chart = ApplicationChart(app, "application")

namespace = k8s.Namespace(namespace_chart, "namespace")
deployment = k8s.Deployment(application_chart, "Deployment")

# dependency between ApiObjects, not Charts!
deployment.add_dependency(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.

### Testing

cdk8s bundles a set of test utilities under the `Testing` class:

* `Testing.app()` returns an `App` object bound to a temporary output directory.
* `Testing.synth(chart)` returns the Kubernetes manifest synthesized from a
  chart.

## License

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

This module is part of the [cdk8s project](https://github.com/awslabs/cdk8s).
"""
import abc
import builtins
import datetime
import enum
import typing

import jsii
import jsii.compat
import publication

import constructs

__jsii_assembly__ = jsii.JSIIAssembly.load("cdk8s", "0.25.0", __name__, "cdk8s@0.25.0.jsii.tgz")


class ApiObject(constructs.Construct, metaclass=jsii.JSIIMeta, jsii_type="cdk8s.ApiObject"):
    """
    stability
    :stability: experimental
    """
    def __init__(self, scope: constructs.Construct, ns: str, *, api_version: str, kind: str, data: typing.Any=None, metadata: typing.Optional["ApiObjectMetadata"]=None) -> None:
        """Defines an API object.

        :param scope: the construct scope.
        :param ns: namespace.
        :param api_version: API version.
        :param kind: Resource kind.
        :param data: Data associated with the resource.
        :param metadata: Object metadata. If ``name`` is not specified, an app-unique name will be allocated by the framework based on the path of the construct within thes construct tree.

        stability
        :stability: experimental
        """
        options = ApiObjectOptions(api_version=api_version, kind=kind, data=data, metadata=metadata)

        jsii.create(ApiObject, self, [scope, ns, options])

    @jsii.member(jsii_name="addDependency")
    def add_dependency(self, *dependencies: constructs.IConstruct) -> None:
        """Create a dependency between this ApiObject and other constructs.

        These can be other ApiObjects, Charts, or custom.

        :param dependencies: the dependencies to add.

        stability
        :stability: experimental
        """
        return jsii.invoke(self, "addDependency", [*dependencies])

    @jsii.member(jsii_name="toJson")
    def to_json(self) -> typing.Any:
        """Renders the object to Kubernetes JSON.

        stability
        :stability: experimental
        """
        return jsii.invoke(self, "toJson", [])

    @builtins.property
    @jsii.member(jsii_name="apiVersion")
    def api_version(self) -> str:
        """The object's API version.

        stability
        :stability: experimental
        """
        return jsii.get(self, "apiVersion")

    @builtins.property
    @jsii.member(jsii_name="chart")
    def chart(self) -> "Chart":
        """The chart in which this object is defined.

        stability
        :stability: experimental
        """
        return jsii.get(self, "chart")

    @builtins.property
    @jsii.member(jsii_name="kind")
    def kind(self) -> str:
        """The object kind.

        stability
        :stability: experimental
        """
        return jsii.get(self, "kind")

    @builtins.property
    @jsii.member(jsii_name="name")
    def name(self) -> str:
        """The name of the API object.

        If a name is specified in ``metadata.name`` this will be the name returned.
        Otherwise, a name will be generated by calling
        ``Chart.of(this).generatedObjectName(this)``, which by default uses the
        construct path to generate a DNS-compatible name for the resource.

        stability
        :stability: experimental
        """
        return jsii.get(self, "name")


@jsii.data_type(jsii_type="cdk8s.ApiObjectMetadata", jsii_struct_bases=[], name_mapping={'name': 'name'})
class ApiObjectMetadata():
    def __init__(self, *, name: typing.Optional[str]=None):
        """Metadata associated with this object.

        :param name: The unique, namespace-global, name of this object inside the Kubernetes cluster. Normally, you shouldn't specify names for objects and let the CDK generate a name for you that is application-unique. The names CDK generates are composed from the construct path components, separated by dots and a suffix that is based on a hash of the entire path, to ensure uniqueness. You can supply custom name allocation logic by overriding the ``chart.generateObjectName`` method. If you use an explicit name here, bear in mind that this reduces the composability of your construct because it won't be possible to include more than one instance in any app. Therefore it is highly recommended to leave this unspecified. Default: - an app-unique name generated by the chart

        stability
        :stability: experimental
        """
        self._values = {
        }
        if name is not None: self._values["name"] = name

    @builtins.property
    def name(self) -> typing.Optional[str]:
        """The unique, namespace-global, name of this object inside the Kubernetes cluster.

        Normally, you shouldn't specify names for objects and let the CDK generate
        a name for you that is application-unique. The names CDK generates are
        composed from the construct path components, separated by dots and a suffix
        that is based on a hash of the entire path, to ensure uniqueness.

        You can supply custom name allocation logic by overriding the
        ``chart.generateObjectName`` method.

        If you use an explicit name here, bear in mind that this reduces the
        composability of your construct because it won't be possible to include
        more than one instance in any app. Therefore it is highly recommended to
        leave this unspecified.

        default
        :default: - an app-unique name generated by the chart

        stability
        :stability: experimental
        """
        return self._values.get('name')

    def __eq__(self, rhs) -> bool:
        return isinstance(rhs, self.__class__) and rhs._values == self._values

    def __ne__(self, rhs) -> bool:
        return not (rhs == self)

    def __repr__(self) -> str:
        return 'ApiObjectMetadata(%s)' % ', '.join(k + '=' + repr(v) for k, v in self._values.items())


@jsii.data_type(jsii_type="cdk8s.ApiObjectOptions", jsii_struct_bases=[], name_mapping={'api_version': 'apiVersion', 'kind': 'kind', 'data': 'data', 'metadata': 'metadata'})
class ApiObjectOptions():
    def __init__(self, *, api_version: str, kind: str, data: typing.Any=None, metadata: typing.Optional["ApiObjectMetadata"]=None):
        """Options for defining API objects.

        :param api_version: API version.
        :param kind: Resource kind.
        :param data: Data associated with the resource.
        :param metadata: Object metadata. If ``name`` is not specified, an app-unique name will be allocated by the framework based on the path of the construct within thes construct tree.

        stability
        :stability: experimental
        """
        if isinstance(metadata, dict): metadata = ApiObjectMetadata(**metadata)
        self._values = {
            'api_version': api_version,
            'kind': kind,
        }
        if data is not None: self._values["data"] = data
        if metadata is not None: self._values["metadata"] = metadata

    @builtins.property
    def api_version(self) -> str:
        """API version.

        stability
        :stability: experimental
        """
        return self._values.get('api_version')

    @builtins.property
    def kind(self) -> str:
        """Resource kind.

        stability
        :stability: experimental
        """
        return self._values.get('kind')

    @builtins.property
    def data(self) -> typing.Any:
        """Data associated with the resource.

        stability
        :stability: experimental
        """
        return self._values.get('data')

    @builtins.property
    def metadata(self) -> typing.Optional["ApiObjectMetadata"]:
        """Object metadata.

        If ``name`` is not specified, an app-unique name will be allocated by the
        framework based on the path of the construct within thes construct tree.

        stability
        :stability: experimental
        """
        return self._values.get('metadata')

    def __eq__(self, rhs) -> bool:
        return isinstance(rhs, self.__class__) and rhs._values == self._values

    def __ne__(self, rhs) -> bool:
        return not (rhs == self)

    def __repr__(self) -> str:
        return 'ApiObjectOptions(%s)' % ', '.join(k + '=' + repr(v) for k, v in self._values.items())


class App(constructs.Construct, metaclass=jsii.JSIIMeta, jsii_type="cdk8s.App"):
    """Represents a cdk8s application.

    stability
    :stability: experimental
    """
    def __init__(self, *, outdir: typing.Optional[str]=None) -> None:
        """Defines an app.

        :param outdir: The directory to output Kubernetes manifests. Default: - CDK8S_OUTDIR if defined, otherwise "dist"

        stability
        :stability: experimental
        """
        options = AppOptions(outdir=outdir)

        jsii.create(App, self, [options])

    @jsii.member(jsii_name="synth")
    def synth(self) -> None:
        """Synthesizes all manifests to the output directory.

        stability
        :stability: experimental
        """
        return jsii.invoke(self, "synth", [])

    @builtins.property
    @jsii.member(jsii_name="outdir")
    def outdir(self) -> str:
        """The output directory into which manifests will be synthesized.

        stability
        :stability: experimental
        """
        return jsii.get(self, "outdir")


@jsii.data_type(jsii_type="cdk8s.AppOptions", jsii_struct_bases=[], name_mapping={'outdir': 'outdir'})
class AppOptions():
    def __init__(self, *, outdir: typing.Optional[str]=None):
        """
        :param outdir: The directory to output Kubernetes manifests. Default: - CDK8S_OUTDIR if defined, otherwise "dist"

        stability
        :stability: experimental
        """
        self._values = {
        }
        if outdir is not None: self._values["outdir"] = outdir

    @builtins.property
    def outdir(self) -> typing.Optional[str]:
        """The directory to output Kubernetes manifests.

        default
        :default: - CDK8S_OUTDIR if defined, otherwise "dist"

        stability
        :stability: experimental
        """
        return self._values.get('outdir')

    def __eq__(self, rhs) -> bool:
        return isinstance(rhs, self.__class__) and rhs._values == self._values

    def __ne__(self, rhs) -> bool:
        return not (rhs == self)

    def __repr__(self) -> str:
        return 'AppOptions(%s)' % ', '.join(k + '=' + repr(v) for k, v in self._values.items())


class Chart(constructs.Construct, metaclass=jsii.JSIIMeta, jsii_type="cdk8s.Chart"):
    """
    stability
    :stability: experimental
    """
    def __init__(self, scope: constructs.Construct, ns: str, *, namespace: typing.Optional[str]=None) -> None:
        """
        :param scope: -
        :param ns: -
        :param namespace: The default namespace for all objects defined in this chart (directly or indirectly). This namespace will only apply to objects that don't have a ``namespace`` explicitly defined for them. Default: - no namespace is synthesized (usually this implies "default")

        stability
        :stability: experimental
        """
        options = ChartOptions(namespace=namespace)

        jsii.create(Chart, self, [scope, ns, options])

    @jsii.member(jsii_name="of")
    @builtins.classmethod
    def of(cls, c: constructs.IConstruct) -> "Chart":
        """Finds the chart in which a node is defined.

        :param c: a construct node.

        stability
        :stability: experimental
        """
        return jsii.sinvoke(cls, "of", [c])

    @jsii.member(jsii_name="addDependency")
    def add_dependency(self, *dependencies: constructs.IConstruct) -> None:
        """Create a dependency between this Chart and other constructs.

        These can be other ApiObjects, Charts, or custom.

        :param dependencies: the dependencies to add.

        stability
        :stability: experimental
        """
        return jsii.invoke(self, "addDependency", [*dependencies])

    @jsii.member(jsii_name="generateObjectName")
    def generate_object_name(self, api_object: "ApiObject") -> str:
        """Generates a app-unique name for an object given it's construct node path.

        Different resource types may have different constraints on names
        (``metadata.name``). The previous version of the name generator was
        compatible with DNS_SUBDOMAIN but not with DNS_LABEL.

        For example, ``Deployment`` names must comply with DNS_SUBDOMAIN while
        ``Service`` names must comply with DNS_LABEL.

        Since there is no formal specification for this, the default name
        generation scheme for kubernetes objects in cdk8s was changed to DNS_LABEL,
        since it’s the common denominator for all kubernetes resources
        (supposedly).

        You can override this method if you wish to customize object names at the
        chart level.

        :param api_object: The API object to generate a name for.

        stability
        :stability: experimental
        """
        return jsii.invoke(self, "generateObjectName", [api_object])

    @jsii.member(jsii_name="toJson")
    def to_json(self) -> typing.List[typing.Any]:
        """Renders this chart to a set of Kubernetes JSON resources.

        return
        :return: array of resource manifests

        stability
        :stability: experimental
        """
        return jsii.invoke(self, "toJson", [])

    @builtins.property
    @jsii.member(jsii_name="namespace")
    def namespace(self) -> typing.Optional[str]:
        """The default namespace for all objects in this chart.

        stability
        :stability: experimental
        """
        return jsii.get(self, "namespace")


@jsii.data_type(jsii_type="cdk8s.ChartOptions", jsii_struct_bases=[], name_mapping={'namespace': 'namespace'})
class ChartOptions():
    def __init__(self, *, namespace: typing.Optional[str]=None):
        """
        :param namespace: The default namespace for all objects defined in this chart (directly or indirectly). This namespace will only apply to objects that don't have a ``namespace`` explicitly defined for them. Default: - no namespace is synthesized (usually this implies "default")

        stability
        :stability: experimental
        """
        self._values = {
        }
        if namespace is not None: self._values["namespace"] = namespace

    @builtins.property
    def namespace(self) -> typing.Optional[str]:
        """The default namespace for all objects defined in this chart (directly or indirectly).

        This namespace will only apply to objects that don't have a
        ``namespace`` explicitly defined for them.

        default
        :default: - no namespace is synthesized (usually this implies "default")

        stability
        :stability: experimental
        """
        return self._values.get('namespace')

    def __eq__(self, rhs) -> bool:
        return isinstance(rhs, self.__class__) and rhs._values == self._values

    def __ne__(self, rhs) -> bool:
        return not (rhs == self)

    def __repr__(self) -> str:
        return 'ChartOptions(%s)' % ', '.join(k + '=' + repr(v) for k, v in self._values.items())


class Include(constructs.Construct, metaclass=jsii.JSIIMeta, jsii_type="cdk8s.Include"):
    """Reads a YAML manifest from a file or a URL and defines all resources as API objects within the defined scope.

    The names (``metadata.name``) of imported resources will be preserved as-is
    from the manifest.

    stability
    :stability: experimental
    """
    def __init__(self, scope: constructs.Construct, name: str, *, url: str) -> None:
        """
        :param scope: -
        :param name: -
        :param url: Local file path or URL which includes a Kubernetes YAML manifest.

        stability
        :stability: experimental
        """
        options = IncludeOptions(url=url)

        jsii.create(Include, self, [scope, name, options])


@jsii.data_type(jsii_type="cdk8s.IncludeOptions", jsii_struct_bases=[], name_mapping={'url': 'url'})
class IncludeOptions():
    def __init__(self, *, url: str):
        """
        :param url: Local file path or URL which includes a Kubernetes YAML manifest.

        stability
        :stability: experimental
        """
        self._values = {
            'url': url,
        }

    @builtins.property
    def url(self) -> str:
        """Local file path or URL which includes a Kubernetes YAML manifest.

        stability
        :stability: experimental

        Example::

            # Example automatically generated without compilation. See https://github.com/aws/jsii/issues/826
            mymanifest.yaml
        """
        return self._values.get('url')

    def __eq__(self, rhs) -> bool:
        return isinstance(rhs, self.__class__) and rhs._values == self._values

    def __ne__(self, rhs) -> bool:
        return not (rhs == self)

    def __repr__(self) -> str:
        return 'IncludeOptions(%s)' % ', '.join(k + '=' + repr(v) for k, v in self._values.items())


class Testing(metaclass=jsii.JSIIMeta, jsii_type="cdk8s.Testing"):
    """Testing utilities for cdk8s applications.

    stability
    :stability: experimental
    """
    @jsii.member(jsii_name="app")
    @builtins.classmethod
    def app(cls) -> "App":
        """Returns an app for testing with the following properties: - Output directory is a temp dir.

        stability
        :stability: experimental
        """
        return jsii.sinvoke(cls, "app", [])

    @jsii.member(jsii_name="chart")
    @builtins.classmethod
    def chart(cls) -> "Chart":
        """
        return
        :return: a Chart that can be used for tests

        stability
        :stability: experimental
        """
        return jsii.sinvoke(cls, "chart", [])

    @jsii.member(jsii_name="synth")
    @builtins.classmethod
    def synth(cls, chart: "Chart") -> typing.List[typing.Any]:
        """Returns the Kubernetes manifest synthesized from this chart.

        :param chart: -

        stability
        :stability: experimental
        """
        return jsii.sinvoke(cls, "synth", [chart])


class Yaml(metaclass=jsii.JSIIMeta, jsii_type="cdk8s.Yaml"):
    """YAML utilities.

    stability
    :stability: experimental
    """
    @jsii.member(jsii_name="load")
    @builtins.classmethod
    def load(cls, url_or_file: str) -> typing.List[typing.Any]:
        """Downloads a set of YAML documents (k8s manifest for example) from a URL or a file and returns them as javascript objects.

        Empty documents are filtered out.

        :param url_or_file: a URL of a file path to load from.

        return
        :return: an array of objects, each represents a document inside the YAML

        stability
        :stability: experimental
        """
        return jsii.sinvoke(cls, "load", [url_or_file])

    @jsii.member(jsii_name="save")
    @builtins.classmethod
    def save(cls, file_path: str, docs: typing.List[typing.Any]) -> None:
        """Saves a set of objects as a multi-document YAML file.

        :param file_path: The output path.
        :param docs: The set of objects.

        stability
        :stability: experimental
        """
        return jsii.sinvoke(cls, "save", [file_path, docs])

    @jsii.member(jsii_name="tmp")
    @builtins.classmethod
    def tmp(cls, docs: typing.List[typing.Any]) -> str:
        """Saves a set of YAML documents into a temp file (in /tmp).

        :param docs: the set of documents to save.

        return
        :return: the path to the temporary file

        stability
        :stability: experimental
        """
        return jsii.sinvoke(cls, "tmp", [docs])


__all__ = ["ApiObject", "ApiObjectMetadata", "ApiObjectOptions", "App", "AppOptions", "Chart", "ChartOptions", "Include", "IncludeOptions", "Testing", "Yaml", "__jsii_assembly__"]

publication.publish()
