Giter VIP home page Giter VIP logo

pulumi-kubernetesx's Introduction

Note: This library is under active development and subject to change. Not yet recommended for production workloads.

Pulumi Kubernetes Extensions

Kubernetes for Everyone

Using the Kubernetes API today often feels heavy and repetitive. Many of the API fields are deeply nested and require users to specify the same values over and over across different resources. While this input is necessary for Kubernetes to operate, it’s not very friendly to the people writing it.

The Kubernetes Extensions (kx) library for Pulumi is designed to simplify the declaration of Kubernetes resources, and make the API easier for everyone to use.

  1. Sane Defaults - Less boilerplate and easier to use. Common configurations require minimal code.
  2. Improved Authorship Experience - Simplified syntax for declaring and composing Kubernetes resources. Reference objects rather than juggling string references across resources.
  3. Idiomatic Kubernetes - We don't reinvent the wheel; no new API resources to learn. We make the existing APIs easier to use while still providing the full API for production use cases.

The kx library takes full advantage of being defined in TypeScript, not in YAML. This enables the use of functions, overloading, type-checking, and many other richer API design tools than are available using YAML or Helm.

If you are just getting started with Pulumi and Kubernetes, the Pulumi Kubernetes introduction is a good place to start.

kx raw provider
kx example raw provider example

kx-up

Installation

This package is available in JavaScript/TypeScript for use with Node.js. Install it using either npm:

$ npm install @pulumi/kubernetesx

or yarn:

$ yarn add @pulumi/kubernetesx

Usage Examples

Define a Pod

Use the PodBuilder class to define a PodSpec that can be used by other kx classes that include a PodSpec (Pod, Deployment, StatefulSet, DaemonSet, ReplicaSet).

const pb = new kx.PodBuilder({
    containers: [{
        // name is not required. If not provided, it is inferred from the image.
        image: "nginx",
        ports: {http: 80}, // Simplified ports syntax.
    }]
});

Note that a PodBuilder does not create a k8s resource; it is a convenience class for defining a PodSpec that can be easily composed with other kx resources.

// Define the PodSpec.
const pb = new kx.PodBuilder({
    containers: [{image: "nginx"}]
});
// Create a Pod resource using the PodBuilder.
new kx.Pod("nginx", {
    spec: pb
});

Create a Deployment

Using a PodBuilder class to define the workload Pod, create a Deployment resource. Instantiating the kx.Deployment class will cause Pulumi to create a matching Deployment resource in your Kubernetes cluster on the next pulumi up.

const pb = new kx.PodBuilder(...);
const deployment = new kx.Deployment("app", {
    // asDeploymentSpec() takes parameters corresponding 
    // to a DeploymentSpec (e.g., replicas).
    spec: pb.asDeploymentSpec({ replicas: 3 }) 
});

Note that you can still define the DeploymentSpec explicitly, but would be responsible for defining required fields (labels/selectors, etc.) as usual. This still benefits from the enhanced kx syntax for env, ports, volumeMounts, and resource composability.

const deployment = new kx.Deployment("app", {
    spec: {
        selector: {
            matchLabels: {
                app: "my-app",
            }
        },
        replicas: 3,
        template: {
            metadata: {
                labels: {
                    app: "my-app",
                }
            },
            spec: {
                containers: [{
                    image: "nginx",
                    ports: {http: 80},
                }]
            }
        }
    }
});

Create a ClusterIP Service from the Deployment

Easily create a Service from a workload using the createService verb.

const deployment = new kx.Deployment(...);
const service = deployment.createService();

Add a PersistentVolumeClaim to a Pod

Use the mount verb on a PersistentVolumeClaim to add it to a Pod under the volumeMounts field. The PodBuilder automatically creates the corresponding volume and naming boilerplate.

const pvc = new kx.PersistentVolumeClaim("data", {
    spec: {
        accessModes: [ "ReadWriteOnce" ],
        resources: { requests: { storage: "1Gi" } }
    }
});
const pb = new kx.PodBuilder({
    containers: [{
        image: "nginx",
        ports: {http: 80},
        volumeMounts: [ pvc.mount("/data") ],
    }]
});

Create Environment Variables from a ConfigMap and Secret

Use the asEnvValue verb on ConfigMap and Secret resources to add them to the Pod under the env field. The PodBuilder automatically creates the relevant boilerplate depending on the resource type.

const cm = new kx.ConfigMap("cm", {
    data: { "config": "very important data" }
});
const secret = new kx.Secret("secret", {
    stringData: { "password": new random.RandomPassword("password", { length: 12 }).result }
});
const pb = new kx.PodBuilder({
    containers: [{
        env: {
            DATA: cm.asEnvValue("config"),
            PASSWORD: secret.asEnvValue("password"),
        },
        image: "nginx",
        ports: {http: 80},
    }]
});

Learn more about Pulumi

Intro to Pulumi

Get Started with Kubernetes

Crosswalk for Kubernetes

Pulumi: A Better Way to Kubernetes

Pulumi's YouTube Channel

pulumi-kubernetesx's People

Contributors

andrebsguedes avatar beetahnator avatar bigkraig avatar lblackstone avatar liamawhite avatar lukehoban avatar metral avatar pgavlin avatar stack72 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  avatar  avatar

pulumi-kubernetesx's Issues

[bug] fails to import: removes ports

Pulumi does not include ports when adopting resulting in a failure to adopt existing resources:
YAML

apiVersion: v1
kind: Service
metadata:
  name: gitea-web
  labels:
    app: gitea-web
spec:
  ports:
    - port: 80
      targetPort: 8080
      name: http
  selector:
    app: gitea

TS
Note that using the kubernetes package for this service will also fail with a different error.

import * as kx from '@pulumi/kubernetesx' // version 0.1.1
const svc = new kx.Service(
  'gitea-web',
  {
    metadata: {
      name: 'gitea-web',
      labels: {
        app: 'gitea-web',
      },
    },
    spec: {
      ports: [
        {
          name: 'http',
          port: 80,
          targetPort: 8080,
        },
      ],
      selector: {
        app: 'gitea',
      },
    },
  },
  {
    import: 'gitea-web',
    ignoreChanges: ['metadata.annotations'],
  }
)

Details:

  pulumi:pulumi:Stack: (same)
    [urn=urn:pulumi:dev::gitkit::pulumi:pulumi:Stack::gitkit-dev]
    = kubernetes:core/v1:Service: (import)
        [id=gitea-web]
        [urn=urn:pulumi:dev::gitkit::kubernetes:core/v1:Service::gitea-web]
        [provider=urn:pulumi:dev::gitkit::pulumi:providers:kubernetes::default_1_3_1::3b7c7381-1acb-47d8-a7fd-dd8563b0de39]
      ~ metadata: {
          - annotations: {}
        }
      ~ spec    : {
          ~ ports: [
              - [0]: {
                      - name      : "http"
                      - port      : 80
                      - targetPort: 8080
                    }
            ]
        }

Also, I appear to be doing something wrong with getting it to ignore annotations.

`createService()` does not match namespace of deployment

When using kubex.Deployment.createService(), the service will always be created in the default namespace.

Example:

import * as kx from "@pulumi/kubernetesx"

const pod = new kx.PodBuilder({
  containers: [
    {
      image: 'abiosoft/caddy'
    }
  ]
})

const deployment = new kx.Deployment('test',
  {
    metadata: {
      namespace: 'kube-system'
    },
    spec: pod.asDeploymentSpec()
  }
)

deployment.createService()

Deployment.createService fails if no ports defined

error: Running program '/Users/levi/workspace/kx-test' failed with an unhandled exception:
    TypeError: Cannot read property 'forEach' of undefined
        at containers.forEach.container (/opt/pulumi/node_modules/@pulumi/kubernetesx/kx.js:173:33)
        at Array.forEach (<anonymous>)
        at serviceSpec.spec.template.spec.containers.apply.containers (/opt/pulumi/node_modules/@pulumi/kubernetesx/kx.js:172:24)
        at /opt/pulumi/node_modules/@pulumi/kubernetesx/node_modules/@pulumi/pulumi/output.js:179:35
        at Generator.next (<anonymous>)
        at /opt/pulumi/node_modules/@pulumi/kubernetesx/node_modules/@pulumi/pulumi/output.js:21:71
        at new Promise (<anonymous>)
        at __awaiter (/opt/pulumi/node_modules/@pulumi/kubernetesx/node_modules/@pulumi/pulumi/output.js:17:12)
        at applyHelperAsync (/opt/pulumi/node_modules/@pulumi/kubernetesx/node_modules/@pulumi/pulumi/output.js:165:12)
        at Promise.all.then (/opt/pulumi/node_modules/@pulumi/kubernetesx/node_modules/@pulumi/pulumi/output.js:159:51)

Let ClusterRoleBinding accept object inputs

Rather than requiring the user to specify the subject and roleRef, implicitly derive this information from input objects.

Example:

   new kx.ClusterRoleBinding(
        name,
        {
            subjects: [
                serviceAccountObject,
            ],
            roleRef: clusterRoleObject,
        }
    );
}

Allow caller to set name of services with `createService` method

Hi! πŸ‘‹

Firstly, thanks for your work on this project! πŸ™‚

Today I used patch-package to patch @pulumi/[email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/@pulumi/kubernetesx/kx.d.ts b/node_modules/@pulumi/kubernetesx/kx.d.ts
index 2bbe265..61b52d9 100644
--- a/node_modules/@pulumi/kubernetesx/kx.d.ts
+++ b/node_modules/@pulumi/kubernetesx/kx.d.ts
@@ -35,6 +35,9 @@ export declare namespace types {
         ports?: pulumi.Input<pulumi.Input<k8s.types.input.core.v1.ServicePort>[] | PortMap>;
         type?: pulumi.Input<ServiceType | string>;
     };
+    type CreateServiceArgs = types.ServiceSpec & {
+        name?: string;
+    };
     type Service = Omit<k8s.types.input.core.v1.Service, "spec"> & {
         spec: pulumi.Input<ServiceSpec>;
     };
@@ -70,7 +73,7 @@ export declare class Deployment extends k8s.apps.v1.Deployment {
     private readonly name;
     private readonly opts?;
     constructor(name: string, args: types.Deployment, opts?: pulumi.CustomResourceOptions);
-    createService(args?: types.ServiceSpec): Service;
+    createService(args?: types.CreateServiceArgs): Service;
 }
 export declare class Service extends k8s.core.v1.Service {
     constructor(name: string, args: types.Service, opts?: pulumi.CustomResourceOptions);
diff --git a/node_modules/@pulumi/kubernetesx/kx.js b/node_modules/@pulumi/kubernetesx/kx.js
index 4aa2874..a43c9a7 100644
--- a/node_modules/@pulumi/kubernetesx/kx.js
+++ b/node_modules/@pulumi/kubernetesx/kx.js
@@ -186,8 +186,9 @@ class Deployment extends k8s.apps.v1.Deployment {
                 // TODO: probably need to unwrap args.type in case it's a computed value
                 type: args && args.type });
         });
-        return new Service(this.name, {
-            metadata: { namespace: this.metadata.namespace },
+        const name = args.name ?? this.name;
+        return new Service(name, {
+            metadata: { namespace: this.metadata.namespace, name },
             spec: serviceSpec,
         }, Object.assign(Object.assign({}, this.opts), { parent: this }));
     }

This issue body was partially generated by patch-package.

[bug] unable to use EnvMap for init container env

When trying to use an EnvMap in an init container, this error is thrown:

Types of property 'env' are incompatible.
  Type '{ "test": string; }' is not assignable to type 'Input<Input<EnvVar>[]>'.
    Type '{ "test": string; }' is missing the following properties from type 'Promise<Input<EnvVar>[]>': then, catch, [Symbol.toStringTag], finally

It appears that an EnvMap is required to properly use the secret asEnvValue function to add a secret environment variable.

It may be a separate bug that asEnvValue configures the value as a value and not a valueFrom when added to a list type env.

README / Docs: Add more examples

Add a couple of examples demonstrating:

  • Using full Service spec to set & create a public LB
  • Talk to defaults done (e.g. names, metadata, ports)
  • Demo how to override any defaults done by PodBuilder

Ability to add/modify labels on a service

Hi,

I'd like to add labels to my kubernetes service, but I don't see this available in kx.Deployment.createService

I tried to work around it with the code below but it had no effect:

service.metadata.labels = {
            ...this.service.metadata.labels,
            "app": this.name
        } as any;

Any suggestions? Lots of other features require specific labels to be set on the service, for scoping purposes.

Thanks in advance!

allow hyphens (-) when auto-naming the container

Affected feature

The generated container names are extracted by the regex /(.*\/|^)(?<image>\w+)(:(?<tag>.*))?/, which skips hyphens. Since most docker images use hyphens rather than underscores (e.g., cert-manager, external-dns, aws-load-banlance-controller), the regex just extracts the first word.

Is there any consideration that stops including hyphens?

Deliver compelling v1 of `kubernetesx` offering

Meta issue to track work needed to deliver a compelling v1 offering for the @pulumi/kubernetesx package.

@metral @CyrusNajmabadi @d-nishi to flesh out with specific goals and to break down into individual work items with owners (like 1 item (5d) to research (w/ design review for conclusions), another (5d) to design (w/ document to review), another (5d) to prototype initial version).

reference: koki short

This is not an issue per-say but an idea for reference.

As you build this simplified Kubernetes API package you may wish to refer to
https://docs.koki.io/short/

They've put some effort into simplifying Kubernetes YAML without information loss. There is likely some useful inspiration there.

Mounting a pvc in a single pod builder multiple times, results in conflicts

Steps to reproduce

  1. create a pvc like in the examples
  2. create a podbuilder with 2 init containers and 1 normal container
  3. all these containers will interact with the data on the pvc so they all have volumeMounts: [ pvc.mount('/data')]
  4. the volumes in the spec builder is not a set, but an array, so it will have 3 volumes for the same pvc
  5. error

Went to a manual Deployment in the end.

Update pulumi-kubernetes dependency to 4.x

Hello!

  • Vote on this issue by adding a πŸ‘ reaction
  • If you want to implement this feature, comment to let us know (we'll work with you on design, scheduling, etc.)

Issue details

  • Update pulumi-kubernetes dependency to the latest 4.x.
  • Include upgrade tests

Affected area/feature

Add an easy way to create a Docker config Secret for private image pulling

export const imagePullSecret = new k8s.core.v1.Secret(
    "docker-hub",
    {
        type: "kubernetes.io/dockerconfigjson",
        metadata: {
            namespace: "community"
        },
        stringData: {
            ".dockerconfigjson": config
                .requireSecret("docker-hub-token")
                .apply(value => {
                    return JSON.stringify({
                        auths: {
                            "https://index.docker.io/v1/": {
                                auth: value
                            }
                        }
                    })
                })
        },
    },
    {
        provider: kubernetesProvider
    }
);

The above snippet is the current way to create a Secret which can be used to pull private Docker images from Docker Hub. Please provide an easier way to create such a secret.

Context:

Issue created on request of @lblackstone

[feature] Include default metadata

Given that a standard pod starts like this

apiVersion: v1
kind: Pod
metadata:
  name: myapp
  labels:
    name: myapp

and a deployment starts like this

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  selector:
    matchLabels:
      app: myapp

I would like to see

new kx.Deployment('name', {
    spec: pod.asDeploymentSpec(),
})

default to set the deployment's metadata property to: { name: firstArg }

asJobSpec has a wrong value for restartPolicy

    const pb = new kx.PodBuilder({
      containers: [
        {
          image: 'bitnami/mongodb:4.4.3-debian-10-r0',
          command: ['/bin/bash', '-c'],
          args: [
            `mongo admin -u root -p 'xxx' --eval "db.getSiblingDB('${namespace.name}').createUser({user: '${namespace.name}', pwd: '${namespace.password}', roles: [{role: 'readWrite', db: '${namespace.name}'}] })"`,
          ],
        },
      ],
    });
    new kx.Job(`create-mongodb-user-${namespace.name}`, {
      spec: pb.asJobSpec({ ttlSecondsAfterFinished: 30, completions: 1 }),
    });
  kubernetes:batch/v1:Job (create-mongodb-user-blue):
    error: resource create-mongodb-user-blue-zs7v5wx8 was not successfully created by the Kubernetes API server : Job.batch "create-mongodb-user-blue-zs7v5wx8" is invalid: spec.template.spec.restartPolicy: Unsupported value: "Always": supported values: "OnFailure", "Never"

Allow caller to set name of services with `createService` method

Hi! πŸ‘‹

Firstly, thanks for your work on this project! πŸ™‚

Today I used patch-package to patch @pulumi/[email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/@pulumi/kubernetesx/kx.d.ts b/node_modules/@pulumi/kubernetesx/kx.d.ts
index 2bbe265..4b677ea 100644
--- a/node_modules/@pulumi/kubernetesx/kx.d.ts
+++ b/node_modules/@pulumi/kubernetesx/kx.d.ts
@@ -35,6 +35,9 @@ export declare namespace types {
         ports?: pulumi.Input<pulumi.Input<k8s.types.input.core.v1.ServicePort>[] | PortMap>;
         type?: pulumi.Input<ServiceType | string>;
     };
+    type CreateServiceArgs = types.ServiceSpec & {
+        metadata?: pulumi.Input<k8s.types.input.meta.v1.ObjectMeta>;
+    };
     type Service = Omit<k8s.types.input.core.v1.Service, "spec"> & {
         spec: pulumi.Input<ServiceSpec>;
     };
@@ -70,7 +73,7 @@ export declare class Deployment extends k8s.apps.v1.Deployment {
     private readonly name;
     private readonly opts?;
     constructor(name: string, args: types.Deployment, opts?: pulumi.CustomResourceOptions);
-    createService(args?: types.ServiceSpec): Service;
+    createService(args?: types.CreateServiceArgs): Service;
 }
 export declare class Service extends k8s.core.v1.Service {
     constructor(name: string, args: types.Service, opts?: pulumi.CustomResourceOptions);
diff --git a/node_modules/@pulumi/kubernetesx/kx.js b/node_modules/@pulumi/kubernetesx/kx.js
index 4aa2874..17f332f 100644
--- a/node_modules/@pulumi/kubernetesx/kx.js
+++ b/node_modules/@pulumi/kubernetesx/kx.js
@@ -186,8 +186,8 @@ class Deployment extends k8s.apps.v1.Deployment {
                 // TODO: probably need to unwrap args.type in case it's a computed value
                 type: args && args.type });
         });
-        return new Service(this.name, {
-            metadata: { namespace: this.metadata.namespace },
+        return new Service(args?.metadata?.name, {
+            metadata: { namespace: this.metadata.namespace, ...args?.metadata },
             spec: serviceSpec,
         }, Object.assign(Object.assign({}, this.opts), { parent: this }));
     }

This issue body was partially generated by patch-package.

Expose Service IP Address in a property

I always forget how to dig through the innards of a Service to find its resulting IP Address. Especially as it differs between clouds. It would be super cool if we had an ipAddress: Output<string> property on kx.Service, which internally encapsulated these details.

Add kx support for initContainers

initContainers currently don't support the simplified kx syntax. This should be unified with the containers support to avoid requiring different syntax.

Related #51

`Service` not applying port params

ports defined in the kx.Service args.spec.ports param don't get created.

import * as kubex from "@pulumi/kubernetesx";

const pod = new kubex.PodBuilder({
  containers: [
    {
      image: 'abiosoft/caddy'
    }
  ]
})

const deployment = new kubex.Deployment(
  'test',
  {
    metadata: {
      namespace: 'kube-system'
    },
    spec: pod.asDeploymentSpec()
  }
)


const service = new kubex.Service(
  'test',
  {
    metadata: { namespace: 'kube-system' },
    spec: {
      selector: {
        app: 'caddy'
      },
      ports: [
        {
          port: 8000,
          name: 'http'
        }
      ]
    }
  }
)

Preview shows no ports being applied

+ kubernetes:core/v1:Service: (create)
+   [urn=xxxxxxx]
+   [provider=xxxxxxxxxxxx]
+   apiVersion: "v1"
+   kind      : "Service"
+   metadata  : {
+       annotations: {
+           pulumi.com/autonamed: "true"
+       }
+       labels     : {
+           app.kubernetes.io/managed-by: "pulumi"
+       }
+       name       : "test-mr4g6lcj"
+       namespace  : "kube-system"
+   }
+   spec      : {
+       selector: {
+           app: "caddy"
+       }
+   }

Feature: Add URL helper to kx.Service

It might be useful to include a helper method that generates a complete URL for a Service.

Something along the lines of:

export const url = service.url( { port: "http" });
url: "http://localhost:80"

Currently this can be accomplished manually with something like

export const endpoint = pulumi.interpolate`http://${service.endpoint}:${service.spec.ports[0].targetPort}`;

Let users control deployment strategy

The other defaults for a kx.Deployment all seem reasonable, but the default Kubernetes Deployment strategy can be a little aggressive with the 25% maxUnavailable.

It'd be nice to be able to pass in such a sensitive configuration.

Add multi-language support

Hello!

  • Vote on this issue by adding a πŸ‘ reaction
  • If you want to implement this feature, comment to let us know (we'll work with you on design, scheduling, etc.)

Issue details

Now that multi-language components are available, we should consider how kubernetesx could be supported in all languages. This might require some changes to the API, which is currently TypeScript-focused.

Cronjob in kx

Unable to define cronjobs as part of kx

Using kx to generate kube YAML for Cronjob

Document the API

This lib doesn't yet include usage docs. Those should be added as the interfaces stabilize.

Deployment pod annotations impossible to add

Affected feature

I needed to add prometheus scrape annotations on the pods in a deployment. There was no way to do so as the template field is omitted in the types. Reverted to a base deployment to get around this.

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.