Fetching Metrics
Kubernetes Client also supports fetching metrics from API server if metrics are enabled on it. You can access metrics via client.top()
. Here are some examples of its usage:
- Get
NodeMetrics
for all nodes:
NodeMetricsList nodeMetricList = client.top().nodes().metrics();
- Get
NodeMetrics
for some specific nodes:
NodeMetrics nodeMetric = client.top().nodes().metrics("minikube");
- Get
PodMetrics
for all pods in all namespaces:
PodMetricsList podMetricsList = client.top().pods().metrics();
- Get
PodMetrics
for all pods in some specific namespace:
PodMetricsList podMetricsList = client.top().pods().metrics("default");
- Get
PodMetrics
for a particular pod:
PodMetrics podMetrics = client.top().pods().metrics("default", "nginx-pod");
Resource API
Kubernetes Client also offers a generic API to handle different kind of Kubernetes resources. Most of the Kubernetes resources in Kubernetes Model are extending a class named HasMetadata
. Resource API can work with any kind of Kubernetes Resource which extends this class. Here are some examples it it's usage:
- Get a Kubernetes Resource from Kubernetes API server:
Pod pod = client.resource(pod1).inNamespace("default").get();
- Create or Replace a Kubernetes Resource:
Pod pod1 = new PodBuilder()
.withNewMetadata().withName("resource-pod-" + RandomStringUtils.randomAlphanumeric(6).toLowerCase(Locale.ROOT)).endMetadata()
.withNewSpec()
.addNewContainer().withName("nginx").withImage("nginx").endContainer()
.endSpec()
.build();
client.resource(pod1).inNamespace("default").createOrReplace();
- Create And Wait until resource is ready:
Pod p = client.resource(pod1).createOrReplaceAnd().waitUntilReady(10, TimeUnit.SECONDS);
- Delete a Kubernetes Resource:
Boolean isDeleted = client.resource(pod1).inNamespace("default").delete();
ResourceList API
Just like generic Kubernetes Resource API, Kubernetes client also provides a generic API to deal with Kubernetes List. Here are some examples of its usage:
- Create or Replace a list of Kubernetes resources:
Service service = new ServiceBuilder()
.withNewMetadata().withName("my-service").endMetadata()
.withNewSpec()
.addToSelector("app", "Myapp")
.addNewPort().withProtocol("TCP").withPort(80).withTargetPort(new IntOrString(9376)).endPort()
.endSpec()
.build();
ConfigMap configMap = new ConfigMapBuilder()
.withNewMetadata().withName("my-configmap").endMetadata()
.addToData(Collections.singletonMap("app", "Myapp"))
.build();
KubernetesList list = new KubernetesListBuilder().withItems(deployment, service, configMap).build();
// Create them for the first time
client.resourceList(list).inNamespace("default").createOrReplace();
- Create or Replace with delete existing resources before creation:
KubernetesList list = new KubernetesListBuilder().withItems(updatedService, updatedConfigMap).build();
client.resourceList(list).inNamespace("default").deletingExisting().createOrReplace();
- Delete a list of items:
Boolean deleted = client.resourceList(new PodListBuilder().withItems(pod1, pod2, pod3).build()).inNamespace("default").delete();
CustomResourceDefinition
CustomResourceDefinition
which are like templates for CustomResource
objects in Kubernetes API are available in Kubernetes Client API via client.customResourceDefinitions()
. Here are some examples of it's common usage:
- Load a
CustomResourceDefinition
from yaml:
CustomResourceDefinition customResourceDefinition = client.customResourceDefinitions().load(new FileInputStream("/sparkapplication-crd.yml")).get();
- Get a
CustomResourceDefinition
from Kubernetes APIServer
CustomResourceDefinition crd = client.customResourceDefinitions().withName("sparkclusters.radanalytics.io").get();
- Create
CustomResourceDefinition
:
CustomResourceDefinition customResourceDefinition = new CustomResourceDefinitionBuilder()
.withApiVersion("apiextensions.k8s.io/v1beta1")
.withNewMetadata().withName("sparkclusters.radanalytics.io")
.endMetadata()
.withNewSpec()
.withNewNames()
.withKind("SparkCluster")
.withPlural("sparkclusters")
.endNames()
.withGroup("radanalytics.io")
.withVersion("v1")
.withScope("Namespaced")
.withNewValidation()
.withNewOpenAPIV3SchemaLike(readSchema())
.endOpenAPIV3Schema()
.endValidation()
.endSpec()
.build();
CustomResourceDefinition crd = client.customResourceDefinitions().createOrReplace(customResourceDefinition);
- Create or Replace some
CustomResourceDefinition
:
CustomResourceDefinition crd = client.customResourceDefinitions().createOrReplace(customResourceDefinition);
- List
CustomResourceDefinition
:
CustomResourceDefinitionList crdList = client.customResourceDefinitions().list();
- Delete
CustomResourceDefinition
:
Boolean deleted = client.customResourceDefinitions().withName("sparkclusters.radanalytics.io").delete();
CustomResource Typed API
CustomResources are available in Kubernetes API via the client.customResources(...)
. In order to use typed API, you need to provide POJOs for your Custom Resource which client can use for serialization/deserialization. client.customResources(...)
take things like CustomResourceDefinitionContext
for locating the CustomResources, CustomResource
class, it's list class etc. It returns an instance of a client which you can use for your CustomResource
related operations. In order to get some idea of how POJOs should look like. Here's an example of POJO for CronTab
CustomResource specified in Kubernetes CustomResource docs my-crontab.yml
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-image
For a CustomResource like this one, we should have a CronTab
java class like this:
Note: Please make sure that your CustomResource POJO is implementing Namespaced
interface if it's a namespaced resource. Otherwise it would be considered a Cluster scoped resource.
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.fabric8.kubernetes.client.mock.crd;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.client.CustomResource;
public class CronTab extends CustomResource implements Namespaced {
private CronTabSpec spec;
private CronTabStatus status;
@Override
public ObjectMeta getMetadata() {
return super.getMetadata();
}
public CronTabSpec getSpec() {
return spec;
}
public void setSpec(CronTabSpec spec) {
this.spec = spec;
}
public CronTabStatus getStatus() {
return status;
}
public void setStatus(CronTabStatus status) {
this.status = status;
}
@Override
public String getApiVersion() {
return "stable.example.com/v1";
}
@Override
public String toString() {
return "CronTab{"+
"apiVersion='" + getApiVersion() + "'" +
", metadata=" + getMetadata() +
", spec=" + spec +
", status=" + status +
"}";
}
}
You can find other helper classes related to CronTab
in our tests. For now, we can proceed with it's common usage examples:
- Get Instance of client for our
CustomResource
:
// Alternatively use CustomResourceDefinitionContext.fromCrd(crd) if you already have a CustomResourceDefinition
CustomResourceDefinitionContext context = new CustomResourceDefinitionContext.Builder()
.withGroup("stable.example.com)
.withVersion("v1")
.withScope("Namespaced")
.withName("crontabs.stable.example.com)
.withPlural("crontabs")
.withKind("CronTab")
.build()
MixedOperation> cronTabClient = client
.customResources(cronTabCrd, CronTab.class, CronTabList.class, DoneableCronTab.class);
- Register your
CustomResource
toKubernetesDeserializer
:
KubernetesDeserializer.registerCustomKind("stable.example.com/v1", "CronTab", CronTab.class);
- Get
CustomResource
from Kubernetes APIServer:
CronTab ct = cronTabClient.inNamespace("default").withName("my-second-cron-object").get();
- Create
CustomResource
:
cronTabClient.inNamespace("default").create(cronTab1);
- List
CustomResource
:
CronTabList cronTabList = cronTabClient.inNamespace("default").list();
- Delete
CustomResource
:
Boolean isDeleted = cronTabClient.inNamespace("default").withName("my-third-cron-object").delete();
- Update Status of
CustomResource
:
cronTabClient.inNamespace("default").updateStatus(updatedCronTab);
- Watch
CustomResource
, (note: You need to register yourCustomResource
toKubernetesDeserializer
otherwise you won't be able to use watch):
cronTabClient.inNamespace("default").watch(new Watcher() {
@Override
public void eventReceived(Action action, CronTab resource) {
// Do something depending upon action type
}
@Override
public void onClose(KubernetesClientException cause) {
}
});
CustomResource Typeless API
Although, you should be using Typed API since it's type-safe. But it can get a bit compilcated to maintain your CustomResource
POJOs and sometimes people don't even have them. Kubernetes Client also provides a typeless/raw API to handle your CustomResource
objects in form of HashMaps. In order to use it, you need to provide it with a CustomResourceDefinitionContext
, which carries necessary information about CustomResource
. Here is an example on how to create one:
- Create
CustomResourceDefinitionContext
:
CustomResourceDefinitionContext customResourceDefinitionContext = new CustomResourceDefinitionContext.Builder()
.withName("animals.jungle.example.com")
.withGroup("jungle.example.com")
.withVersion("v1")
.withPlural("animals")
.withScope("Namespaced")
.build();
Once you have built it, you can pass it to typeless DSL as argument client.customResource(customResourceDefinitionContext)
. With this in place, you can do your standard CustomResource
operations, but you would have to deal with Serialization/Deserialization part yourself. You can convert HashMap to some JSON
object using JSON parsing libraries available on internet.
- Load a
CustomResource
from yaml:
Map customResource = client.customResource(crdContext).load(new FileInputStream("cr.yaml"));
- Get a
CustomResource
from Kubernetes API server:
Map customResourceObject = client.customResource(customResourceDefinitionContext).get(currentNamespace, "otter");
- Create a
CustomResource
:
// Create via file
Map object = client.customResource(customResourceDefinitionContext).create(currentNamespace, new FileInputStream("test-rawcustomresource.yml"));
// Create via raw JSON string
String rawJsonCustomResourceObj = "{\"apiVersion\":\"jungle.example.com/v1\"," +
"\"kind\":\"Animal\",\"metadata\": {\"name\": \"walrus\"}," +
"\"spec\": {\"image\": \"my-awesome-walrus-image\"}}";
Map object = client.customResource(customResourceDefinitionContext).create(currentNamespace, rawJsonCustomResourceObj);
- List
CustomResource
:
Map list = client.customResource(customResourceDefinitionContext).list(currentNamespace);
- List
CustomResource
with labels:
Map list = client.customResource(customResourceDefinitionContext).list(currentNamespace, Collections.singletonMap("foo", "bar"));
- Update
CustomResource
:
Map object = client.customResource(customResourceDefinitionContext).get(currentNamespace, "walrus");
((HashMap)object.get("spec")).put("image", "my-updated-awesome-walrus-image");
object = client.customResource(customResourceDefinitionContext).edit(currentNamespace, "walrus", new ObjectMapper().writeValueAsString(object));
- Delete
CustomResource
:
client.customResource(customResourceDefinitionContext).delete(currentNamespace, "otter");
- Update Status of
CustomResource
:
Map result = client.customResource(customResourceDefinitionContext).updateStatus("ns1", "example-hello", objectAsJsonString);
- Watch
CustomResource
:
final CountDownLatch closeLatch = new CountDownLatch(1);
client.customResource(crdContext).watch(namespace, new Watcher() {
@Override
public void eventReceived(Action action, String resource) {
logger.info("{}: {}", action, resource);
}
@Override
public void onClose(KubernetesClientException e) {
logger.debug("Watcher onClose");
closeLatch.countDown();
if (e != null) {
logger.error(e.getMessage(), e);
}
}
});
closeLatch.await(10, TimeUnit.MINUTES);
CertificateSigningRequest
Kubernetes Client provides using CertificateSigningRequest
via the client.certificateSigningRequests()
DSL interface. Here is an example of creating CertificateSigningRequest
using Fabric8 Kubernetes Client:
try (KubernetesClient client = new DefaultKubernetesClient()) {
CertificateSigningRequest csr = new CertificateSigningRequestBuilder()
.withNewMetadata().withName("test-k8s-csr").endMetadata()
.withNewSpec()
.addNewGroup("system:authenticated")
.withRequest("LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJRWJqQ0NBbFlDQVFBd0tURVBNQTBHQTFVRUF3d0dhMmxrYjI1bk1SWXdGQVlEVlFRS0RBMWtZWFJoTFdWdQpaMmx1WldWeU1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBc2dVZXl0S0F6ZDkyClN1S2pZL1RqbmRsZ0lkSFVVYWFxbHJIVW1nbTloKzY2RTJCNGs0TSt6Q0tOQmovemlMdWV6NFNUeHJ6SFk3RlUKNGUxWElBU3lMS0dmRGNPaks5NThURThwcXBRM21VdlpWMmxnK25BTVF5dlZUYWdZSmFId2JWUzVlNHgvRmpKcQoxTWpQZ1VoSGFXeEdIYTQrQnZYQU9Kdk5BdnV4alpZaVJET251dGxHVzloQkRKRlhoUk5jOGFKNnFiZWVBWnNiCmozWUFMaUcydWp1VmhoTUVRNEJxdFVHVGZCMzBQNGhRK2t2bWVKc2ZUU3Vsb2xiWFdIdVZGWnh1d0FJek5RbmQKMTd4VHd2cU04OGZFb3ZJazBJV0ZCWTk2aHRvaUVNdThZUms4SEZ6QkJralhsZGlkbVNNSHkwK0plcFRONmdQTQpEYVVsd1cxS0lCcW9TbnZNcjY4cFRVWEVhZVRjc040anMxTUIwK3FwR0JBS1puWWVxM0JmMkxVVFBNaG1VZ2VVCmFUTFlqODI2WVorZjJrOWJ1cngwK1NOSmVZbWoxVTl0N3A2YWM0dDIzZHVYQ1BzYkNrUFNKeGtrU3dudUlVVzkKdmJVVGtJNGtVMlFVMnE0NzRaMW1uMlkvejF2TEdQdEpsTDFYUVFVNEdsb2hrQkVkM1BsUTRtOGU1WGZSRkV6ZgpYZnhMRXFRczFTeEg1ekhjcnVaOWxJdnBkeEw5Tkc5WlR6M0tmT0tIbCtSUzdxMGdKaExac0RubUJKNXZab3p4CldXci9IRW9PamFYbGh0VitDN3M4TUg5Y0lKZENZNnpjcFVrZis1NmZ0Z1FuN0YrT1RYdDI0UVJQYWNFZnRFOTkKVERPb2luTGtOMm1kckxiMTgxQUZNUWJ0bTFLc1k2MENBd0VBQWFBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQwpBUUNQYU1WdDd4YWhkZlF1L1BySFVTTW5LK2I0SlJScEdEYlpWUXk4aUtkSmdHM0VrYnNBZ21qQmN4Q1IvL2t1CkVhU0plSGNWK20xVlFUTEp1ZFU3ZHFUeFBLOVFCNlB2aHlBbCttNnFaQkt1Q25VM1BKc2k5azBYSE5GWXBqRmYKVFNwTlpJSnRuanVEQWVtT05kcjJYMm1rODZmSmpWTEUvYnA1KzM5dFBkN0xjL3dZR2JoRU0xcExtUGpQK0Z6eQpzZnBiYW5PcmZFSG5NMmlsRFpGZURVSEpYL3F5Ykt1RC9BRmdoZk1Ua0x3ODNLNkNRdCtDQm05djRCeEtCS2xqCkdBWEQyUEhUTWlzektUbGpBM3czYUphanZzU0FwQXFCWnFocjB3QzdOb1dYM1h6S0p3ck9MaWVxemo3SXlpUGEKTEI5SmJveFpOQTdBSU5ucEdsa1hDZlRGT2RManZtQkVRQXV5Ym9wLzdqV2RiSzJHRkZKS2UwdlVlbWNUeGdHVwp5c0ZyV2pqMUlvdVBVNFZ6ck82QVBVQnZCZUFtdU1Bbm9yVng5emc4akhlT1pkd2RWdFRnOUwrK0VnWjlxK0htCjVtUlJGVHlZOWo4WVVvd2J6TzRlRUZnaVN0di84T1p0YmtOeDFROWFQWHJ3VUV1Q1I0SUthWG0wNlJUYXJOYXUKTWFsbk5oZm9WYi9Bc1R5d1ArNlc1dGErcTBpckR5cnVkZk5pRkFWbkRMZEU5a2hWZzVrU0lPRzhYbEZUMklwSQpkdVNpcUl0NlNUTlY3UmdaRzBGTFN5akxoc3laWnY2bitpUzl3Ky9OOFpoUzgvalViUUVidG1VTnNJU3Z5WS9JCmZqcHNZQUdleExvVW5mN2pDaUhkbTVhSnJ5SU1kdmZ2akJsMDhIVk5nWG1McVE9PQotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0K")
.addNewUsage("client auth")
.endSpec()
.build();
client.certificateSigningRequests().create(csr);
}
SharedInformers
Kubernetes Client also provides SharedInformer
support in order to stay updated to events happening to your resource inside Kubernetes. It's implementation is just list and watch operations after a certain interval of time. Here are some of the common usages:
- Get
SharedInformerFactory
:
SharedInformerFactory sharedInformerFactory = client.informers();
- Create
SharedIndexInformer
for some Kubernetes Resource(requires resource's class, resource's list class, and resync period(when to check with server again while watching something). By default it watches in all namespaces.:
SharedIndexInformer podInformer = sharedInformerFactory.sharedIndexInformerFor(Pod.class, PodList.class, 30 * 1000L);
podInformer.addEventHandler(new ResourceEventHandler() {
@Override
public void onAdd(Pod pod) {
logger.info("{} pod added", pod.getMetadata().getName());
}
@Override
public void onUpdate(Pod oldPod, Pod newPod) {
logger.info("{} pod updated", oldPod.getMetadata().getName());
}
@Override
public void onDelete(Pod pod, boolean deletedFinalStateUnknown) {
logger.info("{} pod deleted", pod.getMetadata().getName());
}
});
- Create
SharedIndexInformer
for some Custom Resource(in our case,Dummy
resource provided in our examples . By default it watches in all namespaces.
CustomResourceDefinitionContext crdContext = new CustomResourceDefinitionContext.Builder()
.withVersion("v1")
.withScope("Namespaced")
.withGroup("demo.fabric8.io")
.withPlural("dummies")
.build();
SharedIndexInformer dummyInformer = sharedInformerFactory.sharedIndexInformerForCustomResource(crdContext, Dummy.class, DummyList.class, 1 * 60 * 1000);
dummyInformer.addEventHandler(new ResourceEventHandler() {
@Override
public void onAdd(Dummy dummy) {
System.out.printf("%s dummy added\n", dummy.getMetadata().getName());
}
@Override
public void onUpdate(Dummy oldDummy, Dummy newDummy) {
System.out.printf("%s dummy updated\n", oldDummy.getMetadata().getName());
}
@Override
public void onDelete(Dummy dummy, boolean deletedFinalStateUnknown) {
System.out.printf("%s dummy deleted \n", dummy.getMetadata().getName());
}
});
- Create namespaced
SharedIndexInformer
(informers specific to a particularNamespace
):
SharedInformerFactory sharedInformerFactory = client.informers();
SharedIndexInformer podInformer = sharedInformerFactory.sharedIndexInformerFor(
Pod.class,
PodList.class,
new OperationContext().withNamespace("default"),
30 * 1000L);
logger.info("Informer factory initialized.");
podInformer.addEventHandler(new ResourceEventHandler() {
@Override
public void onAdd(Pod pod) {
logger.info("Pod " + pod.getMetadata().getName() + " got added");
}
@Override
public void onUpdate(Pod oldPod, Pod newPod) {
logger.info("Pod " + oldPod.getMetadata().getName() + " got updated");
}
@Override
public void onDelete(Pod pod, boolean deletedFinalStateUnknown) {
logger.info("Pod " + pod.getMetadata().getName() + " got deleted");
}
});
- Create Namespaced Informer for a Custom Resource(Note: Your CustomResource POJO must implement
Namespaced
interface like the one used in this example: Dummy.java)
CustomResourceDefinitionContext crdContext = new CustomResourceDefinitionContext.Builder()
.withVersion("v1")
.withScope("Namespaced")
.withGroup("demo.fabric8.io")
.withPlural("dummies")
.build();
SharedIndexInformer dummyInformer = sharedInformerFactory.sharedIndexInformerForCustomResource(crdContext,
Dummy.class,
DummyList.class,
new OperationContext().withNamespace("default"), // Namespace to watch
1 * 60 * 1000);
dummyInformer.addEventHandler(new ResourceEventHandler() {
@Override
public void onAdd(Dummy dummy) {
System.out.printf("%s dummy added\n", dummy.getMetadata().getName());
}
@Override
public void onUpdate(Dummy oldDummy, Dummy newDummy) {
System.out.printf("%s dummy updated\n", oldDummy.getMetadata().getName());
}
@Override
public void onDelete(Dummy dummy, boolean deletedFinalStateUnknown) {
System.out.printf("%s dummy deleted \n", dummy.getMetadata().getName());
}
});
- Start all registered informers:
sharedInformerFactory.startAllRegisteredInformers();
- Stop all registered informers:
sharedInformerFactory.stopAllRegisteredInformers();
List Options
There are various options provided by Kubernetes Client API when it comes to listing resources. Here are some of the common examples provided:
- List with pagination, comes with limit and continue parameters. The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize.
PodList podList = client.pods().inNamespace("myproject").list(5, null);
podList = client.pods().inNamespace("myproject").list(5, podList.getMetadata().getContinue());
- List resources containing some specific Label:
PodList podList = client.pods().inNamespace("default").withLabel("foo", "bar").list();
- List resources containing a set of labels:
PodList podList = client.pods().inNamespace("default").withLabels(Collections.singletonMap("foo", "bar")).list();
- List resources without Label:
PodList podList = client.pods().inNamespace("default").withoutLabel("foo", "bar").list();
- List resources without a set of Labels:
PodList podList = client.pods().inNamespace("default").withoutLabels(Collections.singletonMap("foo", "bar")).list();
- List resources with labels in:
PodList podList = client.pods().inNamespace("default").withLabelIn("foo", "bar").list();
- List resources with labels not in:
PodList podList =client.pods().inNamespace("default").withLabelNotIn("foo", "bar").list();
- List resources with field:
PodList podList = client.pods().inNamespace("default").withField("foo", "bar").list();
- List resources with fields:
PodList podList = client.pods().inNamespace("default").withFields(Collections.singletonMap("foo", "bar")).list();
- List resources without field:
PodList podList = client.pods().inNamespace("default").withoutField("foo", "bar").list();
- List resources without fields:
PodList podList = client.pods().inNamespace("default").withoutFields(Collections.singletonMap("foo", "bar")).list();
- List resources with
ListOptions
:
PodList podList = client.pods().inNamespace("default").list(new ListOptionsBuilder()
.withLimit(1L)
.withContinue(null)
.build());
Delete Options
Kubernetes Client also provides way to delete dependents of some Kubernetes resource. Here are some examples:
- Providing
cascading()
in order to either delete dependent resources or leave them orphan. By default it istrue
meaning it would delete dependent resources too.
Boolean isDeleted = client.apps().deployments().inNamespace("default").withName("nginx-deploy").cascading(true).delete();
- Providing
propagationPolicy(..)
to specify how deletion should be performed:
Boolean isDeleted = client.apps().deployments().inNamespace("default").withName("nginx-deploy").withPropagationPolicy("Foreground").delete();
- Specifying grace period for deletion:
Boolean isDeleted = client.apps().deployments().inNamespace("ns1").withName("mydeployment").withPropagationPolicy(DeletionPropagation.FOREGROUND).withGracePeriod(10).delete();
Watch Options
Kubernetes Client provides namely three different ways of using Watch
:
- Plain
Watch
without any arguments:
client.pods().inNamespace(namespace).watch(new Watcher() {
@Override
public void eventReceived(Action action, Pod pod) {
logger.log(Level.INFO, action.name() + " " + pod.getMetadata().getName());
switch (action.name()) {
case "ADDED":
logger.log(Level.INFO, pod.getMetadata().getName() + "got added");
break;
case "DELETED":
logger.log(Level.INFO, pod.getMetadata().getName() + "got deleted");
break;
case "MODIFIED":
logger.log(Level.INFO, pod.getMetadata().getName() + "got modified");
break;
default:
logger.log(Level.SEVERE, "Unrecognized event: " + action.name());
}
}
@Override
public void onClose(KubernetesClientException e) {
logger.log(Level.INFO, "Closed");
isWatchClosed.countDown();
}
});
// Wait till watch gets closed
isWatchClosed.await();
- Deprecated : Watching with
resourceVersion
provided:
String resourceVersion = "20012";
client.pods().inNamespace(namespace).watch(resourceVersion, new Watcher() {
@Override
public void eventReceived(Action action, Pod pod) {
logger.log(Level.INFO, action.name() + " " + pod.getMetadata().getName());
switch (action.name()) {
case "ADDED":
logger.log(Level.INFO, pod.getMetadata().getName() + "got added");
break;
case "DELETED":
logger.log(Level.INFO, pod.getMetadata().getName() + "got deleted");
break;
case "MODIFIED":
logger.log(Level.INFO, pod.getMetadata().getName() + "got modified");
break;
default:
logger.log(Level.SEVERE, "Unrecognized event: " + action.name());
}
}
@Override
public void onClose(KubernetesClientException e) {
logger.log(Level.INFO, "Closed");
isWatchClosed.countDown();
}
});
// Wait till watch gets closed
isWatchClosed.await();
} catch (InterruptedException interruptedException) {
logger.log(Level.INFO, "Thread Interrupted!");
Thread.currentThread().interrupt();
}
- Watching with
ListOptions
object:
client.pods().watch(new ListOptionsBuilder().withTimeoutSeconds(30L).build(), new Watcher() {
@Override
public void eventReceived(Action action, Pod resource) { }
@Override
public void onClose(KubernetesClientException cause) { }
});
Log Options
- Get logs with pretty output:
client.pods().inNamespace("test").withName("foo").withPrettyOutput().getLog();
- Get logs of a specific container:
client.pods().inNamespace("test").withName("foo").inContainer("container1").getLog();
- Get logs for the previous instance of the container in a pod if it exists:
client.pods().inNamespace("test").withName("foo").terminated().getLog();
- Only return logs after a specific date (RFC3339):
client.pods().inNamespace("test").withName("foo").sinceTime("2020-09-10T12:53:30.154148788Z").getLog();
- Get logs after a duration of seconds:
client.pods().inNamespace("test").withName("foo").sinceSeconds(10).getLog();
- Get logs lines of recent log file to display.
client.pods().inNamespace("test").withName("foo").tailingLines(10).getLog();
- Configure Maximum bytes of logs to return. Defaults to no limit.
client.pods().inNamespace("test").withName("foo").limitBytes(102).getLog();
- Include timestamps on each line in the log output
client.pods().inNamespace("test").withName("foo").usingTimestamps().getLog();
Serializing to yaml
Resources can be exported to a yaml String via the SerializationUtils
class:
Pod myPod;
String myPodAsYaml = SerializationUtils.dumpAsYaml(myPod);
// Your pod might have some state that you don't really care about, to remove it:
String myPodAsYamlWithoutRuntimeState = SerializationUtils.dumpWithoutRuntimeStateAsYaml(myPod);
Running a Pod
Kubernetes Client also provides mechanism similar to kubectl run
in which you can spin a Pod
just by specifying it's image and name:
- Running a
Pod
by just providing image and name:
try (KubernetesClient client = new DefaultKubernetesClient()) {
client.run().inNamespace("default")
.withName("hazelcast")
.withImage("hazelcast/hazelcast:3.12.9")
.done();
}
- You can also provide slighly complex configuration with
withGeneratorConfig
method in which you can specify labels, environment variables, ports etc:
try (KubernetesClient client = new DefaultKubernetesClient()) {
client.run().inNamespace("default")
.withRunConfig(new RunConfigBuilder()
.withName("nginx")
.withImage("nginx:latest")
.withLabels(Collections.singletonMap("foo", "bar"))
.withEnv(Collections.singletonMap("KUBERNETES_TEST", "fabric8"))
.build())
.done();
}
OpenShift Client DSL Usage
Fabric8 Kubernetes Client also has an extension for OpenShift. It is pretty much the same as Kubernetes Client but has support for some additional OpenShift resources.
Initializing OpenShift Client:
Initializing OpenShift client is the same as Kubernetes Client. Yo
try (final OpenShiftClient client = new DefaultOpenShiftClient()) {
// Do stuff with client
}
This would pick up default settings, reading your kubeconfig
file from ~/.kube/config
directory or whatever is defined inside KUBECONFIG
environment variable. But if you want to customize creation of client, you can also pass a Config
object inside DefaultKubernetesClient
like this:
Config kubeConfig = new ConfigBuilder()
.withMasterUrl("https://api.ci-ln-3sbdl1b-d5d6b.origin-ci-int-aws.dev.examplecloud.com:6443")
.withOauthToken("xxxxxxxx-41oafKI6iU637-xxxxxxxxxxxxx")
.build())) {
try (final OpenShiftClient client = new DefaultOpenShiftClient(config)) {
// Do stuff with client
}
You can also create OpenShiftClient
from an existing instance of KubernetesClient
. There is a method called adapt(..)
for this. Here is an example:
KubernetesClient client = new DefaultKubernetesClient();
OpenShiftClient openShiftClient = client.adapt(OpenShiftClient.class);
DeploymentConfig
DeploymentConfig
is available in OpenShift client via client.deploymentConfigs()
. Here are some examples of its common usage:
- Load
DeploymentConfig
from yaml:
DeploymentConfig deploymentConfig = client.deploymentConfigs().inNamespace(currentNamespace)
.load(new FileInputStream("test-deploymentconfig.yml")).get();
- Get
DeploymentConfig
from OpenShift API server:
DeploymentConfig dc = client.deploymentConfigs().inNamespace(currentNamespace).withName("deploymentconfig1").get();
- Create
DeploymentConfig
:
DeploymentConfig dc = new DeploymentConfigBuilder()
.withNewMetadata().withName("deploymentconfig1").endMetadata()
.withNewSpec()
.withReplicas(2)
.withNewTemplate()
.withNewMetadata()
.addToLabels("app", "database")
.endMetadata()
.withNewSpec()
.addNewContainer()
.withName("mysql")
.withImage("openshift/mysql-55-centos7")
.endContainer()
.endSpec()
.endTemplate()
.endSpec()
.build();
DeploymentConfig dcCreated = client.deploymentConfigs().inNamespace("default").create(dc);
- Create or Replace an existing
DeploymentConfig
:
DeploymentConfig dc = client.deploymentConfigs().inNamespace("default").createOrReplace(dcToCreate);
- List
DeploymentConfig
in some namespace:
DeploymentConfigList aDeploymentConfigList = client.deploymentConfigs().inNamespace("default").list();
- List
DeploymentConfig
in any namespace:
DeploymentConfigList dcList = client.deploymentConfigs().inAnyNamespace().list();
- List
DeploymentConfig
in some namespace with some label:
DeploymentConfigList dcList = client.deploymentConfigs().inNamespace("default").withLabel("foo", "bar").list();
- Update
DeploymentConfig
:
DeploymentConfig deploymentConfig1 = client.deploymentConfigs().inNamespace(currentNamespace).withName("deploymentconfig1").edit()
.editSpec().withReplicas(3).endSpec().done();
- Delete
DeploymentConfig
:
Boolean bDeleted = client.deploymentConfigs().inNamespace("default").withName("deploymentconfig1").delete();
- Watch
DeploymentConfig
:
client.deploymentConfigs().inNamespace("default").watch(new Watcher() {
@Override
public void eventReceived(Action action, DeploymentConfig resource) {
// Do something depending upon action
}
@Override
public void onClose(KubernetesClientException cause) {
}
});
BuildConfig
BuildConfig
resource is available in OpenShift Client via client.buildConfigs()
. Here are some examples of it's common uses:
- Load
BuildConfig
from yaml:
BuildConfig aBuildConfig = client.buildConfigs().inNamespace(currentNamespace)
.load(new FileInputStream("/test-buildconfig.yml")).get();
- Get
BuildConfig
from OpenShift API server:
BuildConfig bc = client.buildConfigs().inNamespace(currentNamespace).withName("bc1").get();
- Create
BuildConfig
:
BuildConfig buildConfig1 = new BuildConfigBuilder()
.withNewMetadata().withName("bc1").endMetadata()
.withNewSpec()
.addNewTrigger()
.withType("GitHub")
.withNewGithub()
.withSecret("secret101")
.endGithub()
.endTrigger()
.addNewTrigger()
.withType("Generic")
.withNewGeneric()
.withSecret("secret101")
.endGeneric()
.endTrigger()
.addNewTrigger()
.withType("ImageChange")
.endTrigger()
.withNewSource()
.withType("Git")
.withNewGit()
.withUri("https://github.com/openshift/ruby-hello-world")
.endGit()
.withDockerfile("FROM openshift/ruby-22-centos7\\nUSER example")
.endSource()
.withNewStrategy()
.withType("Source")
.withNewSourceStrategy()
.withNewFrom()
.withKind("ImageStreamTag")
.withName("origin-ruby-sample:latest")
.endFrom()
.endSourceStrategy()
.endStrategy()
.withNewOutput()
.withNewTo()
.withKind("ImageStreamTag")
.withName("origin-ruby-sample:latest")
.endTo()
.endOutput()
.withNewPostCommit()
.withScript("bundle exec rake test")
.endPostCommit()
.endSpec()
.build();
client.buildConfigs().inNamespace("default").create(buildConfig1);
- Create or Replace
BuildConfig
:
BuildConfig bc = client.buildConfigs().inNamespace("default").create(buildConfig1);
- List
BuildConfig
in some namespace:
BuildConfigList bcList = client.buildConfigs().inNamespace("default").list();
- List
BuildConfig
in any namespace:
BuildConfigList bcList = client.buildConfigs().inNamespace("default").list();
- List
BuildConfig
in some namespace with some label:
BuildConfigList bcList = client.buildConfigs().inNamespace("default").withLabel("foo", "bar").list();
- Delete
BuildConfig
:
Boolean bDeleted = client.buildConfigs().inNamespace("default").withName("bc1").delete();
- Watch
BuildConfig
:
client.buildConfigs().inNamespace("default").watch(new Watcher() {
@Override
public void eventReceived(Action action, BuildConfig resource) {
// Do something depending upon action type
}
@Override
public void onClose(KubernetesClientException cause) {
}
});
Route
Route
resource is available in OpenShift client API via client.routes()
. Here are some examples of its common usage:
- Load
Route
from yaml:
Route aRoute = client.routes().inNamespace("default").load(new FileInputStream("test-route.yml")).get();
- Get
Route
from OpenShift API server:
Route route1 = client.routes().inNamespace("default").withName("route1").get();
- Create
Route
:
Route route1 = new RouteBuilder()
.withNewMetadata().withName("route1").endMetadata()
.withNewSpec().withHost("www.example.com").withNewTo().withKind("Service").withName("service-name1").endTo().endSpec()
.build();
client.routes().inNamespace("defalt").create(route1);
- Create or Replace
Route
:
Route route = client.routes().inNamespace("default").createOrReplace(route1);
- List
Route
in some namespace:
RouteList aRouteList = client.routes().inNamespace("default").list();
- List
Route
in any namespace:
RouteList aRouteList = client.routes().inAnyNamespace().list();
- List
Route
in some namespace with some label:
RouteList routeList = client.routes().inNamespace("default").withLabel("foo", "bar").list();
- Delete
Route
:
boolean bDeleted = client.routes().inNamespace("default").withName("route1").delete();
Project
OpenShift Project
resource can be found in OpenShift Client API via client.projects()
. Here are some examples of its usage:
- Get
Project
:
Project myProject = client.projects().withName("default").get();
- Create
Project
ProjectRequest request = client.projectrequests().createNew().withNewMetadata().withName("thisisatest").endMetadata().withDescription("Fabric8").withDisplayName("Fabric8).done();
- List
Project
ProjectList projectList = client.projects().list();
- Delete
Project
:
Boolean isDeleted = client.projects().withName("default").delete();
ImageStream
ImageStream
resource is available in OpenShift client via client.imageStreams()
. Here are some examples of its common usage:
- Load
ImageStream
from yaml:
ImageStream aImageStream = client.imageStreams()
.load(new FileInputStream("test-imagestream.yml")).get();
- Get
ImageStream
from OpenShift API Server:
ImageStream is =client.imageStreams().inNamespace("default").withName("example-camel-cdi").get();
- Create
ImageStream
:
ImageStream imageStream1 = new ImageStreamBuilder()
.withNewMetadata()
.withName("example-camel-cdi")
.endMetadata()
.withNewSpec()
.addNewTag()
.withName("latest")
.endTag()
.withDockerImageRepository("fabric8/example-camel-cdi")
.endSpec()
.withNewStatus().withDockerImageRepository("").endStatus()
.build();
client.imageStreams().inNamespace("default").create(imageStream1);
- Create or Replace
ImageStream
:
ImageStream is = client.imageStreams().inNamespace("default").createOrReplace(imageStream1);
- List
ImageStream
in some namespace:
ImageStreamList aImageStreamList = client.imageStreams().inNamespace("default").list();
- List
ImageStream
in any namespace:
ImageStreamList isList = client.imageStreams().inAnyNamespace().list();
- List
ImageStream
in some namespace with some labels:
ImageStreamList isList = client.imageStreams().inNamespace("default").withLabel("foo", "bar").list();
- Delete
ImageStream
:
Boolean bDeleted = client.imageStreams().inNamespace("default").withName("example-camel-cdi").delete();
CatalogSource
CatalogSource
is available for usage in OpenShift Client via client.operatorHub().catalogSources()
. Here are some common examples of it's usage:
- Load
CatalogSource
from YAML:
CatalogSource cs = client.operatorHub().catalogSources()
.load(new FileInputStream("/test-catalogsource.yml").get();
- Create
CatalogSource
:
CatalogSource cs = new CatalogSourceBuilder()
.withNewMetadata().withName("foo").endMetadata()
.withNewSpec()
.withSourceType("Foo")
.withImage("nginx:latest")
.withDisplayName("Foo Bar")
.withPublisher("Fabric8")
.endSpec()
.build();
client.operatorHub().catalogSources().inNamespace("default").createOrReplace(cs);
- List
CatalogSource
in some namespace:
CatalogSourceList csList = client.operatorHub().catalogSources().inNamespace("ns1").list();
- List
CatalogSource
in any namespace:
CatalogSourceList csList = client.operatorHub().catalogSources().inAnyNamespace().list();
- List
CatalogSource
in some namespace with some labels:
CatalogSourceList csList = client.operatorHub().catalogSources().inNamespace("default").withLabel("foo", "bar").list();
- Delete
CatalogSource
:
client.operatorHub().catalogSources().inNamespace("default").withName("foo").delete();
PrometheusRule
PrometheusRule
is available for usage in OpenShift Client via client.monitoring().prometheusRules()
. Here are some common examples of it's usage:
- Load
PrometheusRule
from YAML:
PrometheusRule prometheusRule = client.monitoring().prometheusRules()
.load(new FileInputStream("/test-prometheusrule.yml").get();
- Create
PrometheusRule
:
PrometheusRule prometheusRule = new PrometheusRuleBuilder()
.withNewMetadata().withName("foo").endMetadata()
.withNewSpec()
.addNewGroup()
.withName("./example-rules")
.addNewRule()
.withAlert("ExampleAlert")
.withNewExpr().withStrVal("vector(1)").endExpr()
.endRule()
.endGroup()
.endSpec()
.build();
client.monitoring().prometheusRules().inNamespace("default").createOrReplace(prometheusRule);
- List
PrometheusRule
in some namespace:
PrometheusRuleList prList = client.monitoring().prometheusRules().inNamespace("ns1").list();
- List
PrometheusRule
in any namespace:
PrometheusRuleList prList = client.monitoring().prometheusRules().inAnyNamespace().list();
- List
PrometheusRule
in some namespace with some labels:
PrometheusRuleList prList = client.monitoring().prometheusRules().inNamespace("default").withLabel("foo", "bar").list();
- Delete
PrometheusRule
:
client.monitoring().prometheusRules().inNamespace("default").withName("foo").delete();
ServiceMonitor
ServiceMonitor
is available for usage in OpenShift Client via client.monitoring().serviceMonitors()
. Here are some common examples of it's usage:
- Load
ServiceMonitor
from YAML:
ServiceMonitor serviceMonitor = client.monitoring().serviceMonitors()
.load(new FileInputStream("/test-servicemonitor.yml").get();
- Create
ServiceMonitor
:
ServiceMonitor serviceMonitor = new ServiceMonitorBuilder()
.withNewMetadata()
.withName("foo")
.addToLabels("prometheus", "frontend")
.endMetadata()
.withNewSpec()
.withNewNamespaceSelector().withAny(true).endNamespaceSelector()
.withNewSelector()
.addToMatchLabels("prometheus", "frontend")
.endSelector()
.addNewEndpoint()
.withPort("http-metric")
.withInterval("15s")
.endEndpoint()
.endSpec()
.build();
client.monitoring().serviceMonitors().inNamespace("rokumar").createOrReplace(serviceMonitor)
- List
ServiceMonitor
in some namespace:
ServiceMonitorList serviceMonitorList = client.monitoring().serviceMonitors().inNamespace("ns1").list();
- List
ServiceMonitor
in any namespace:
ServiceMonitorList serviceMonitorList = client.monitoring().serviceMonitors().inAnyNamespace().list();
- List
ServiceMonitor
in some namespace with some labels:
ServiceMonitorList serviceMonitorList = client.monitoring().catalogSources().inNamespace("default").withLabel("foo", "bar").list();
- Delete
ServiceMonitor
:
client.operatorHub().monitoring().inNamespace("default").withName("foo").delete();
ClusterResourceQuota
- Create
ClusterResourceQuota
:
try (OpenShiftClient client = new DefaultOpenShiftClient()) {
Map hard = new HashMap<>();
hard.put("pods", new Quantity("10"));
hard.put("secrets", new Quantity("20"));
ClusterResourceQuota acrq = new ClusterResourceQuotaBuilder()
.withNewMetadata().withName("foo").endMetadata()
.withNewSpec()
.withNewSelector()
.addToAnnotations("openshift.io/requester", "foo-user")
.endSelector()
.withQuota(new ResourceQuotaSpecBuilder()
.withHard(hard)
.build())
.endSpec()
.build();
client.quotas().clusterResourceQuotas().createOrReplace(acrq);
}
- List
ClusterResourceQuota
from server:
ClusterResourceQuotaList clusterResourceQuotaList = client.quotas().clusterResourceQuotas().list();
- Delete
ClusterResourceQuota
:
client.quotas().clusterResourceQuotas().withName("foo").delete();
ClusterVersion
- Fetch Cluster Version:
try (OpenShiftClient client = new DefaultOpenShiftClient()) {
ClusterVersion clusterVersion = client.config().clusterVersions().withName("version").get();
System.out.println("Cluster Version: " + clusterVersion.getStatus().getDesired().getVersion());
}
EgressNetworkPolicy
EgressNetworkPolicy
is available for usage in OpenShift Client via client..egressNetworkPolicys()
. Here are some common examples of it's usage:
- Load
EgressNetworkPolicy
from YAML:
EgressNetworkPolicy egressNetworkPolicy = client.egressNetworkPolicies()
.load(new FileInputStream("/test-enp.yml").get();
- Create
EgressNetworkPolicy
:
try (OpenShiftClient client = new DefaultOpenShiftClient()) {
EgressNetworkPolicy enp = new EgressNetworkPolicyBuilder()
.withNewMetadata()
.withName("foo")
.withNamespace("default")
.endMetadata()
.withNewSpec()
.addNewEgress()
.withType("Allow")
.withNewTo()
.withCidrSelector("1.2.3.0/24")
.endTo()
.endEgress()
.addNewEgress()
.withType("Allow")
.withNewTo()
.withDnsName("www.foo.com")
.endTo()
.endEgress()
.endSpec()
.build();
client.egressNetworkPolicies().inNamespace("default").createOrReplace(enp);
}
- List
EgressNetworkPolicy
in some namespace:
EgressNetworkPolicyList egressNetworkPolicyList = client.egressNetworkPolicies().inNamespace("default").list();
- List
EgressNetworkPolicy
in any namespace:
EgressNetworkPolicyList egressNetworkPolicyList = client.egressNetworkPolicies().inAnyNamespace().list();
- List
EgressNetworkPolicy
in some namespace with some labels:
EgressNetworkPolicyList egressNetworkPolicyList = client.egressNetworkPolicies().inNamespace("default").withLabel("foo", "bar").list();
- Delete
EgressNetworkPolicy
:
client.egressNetworkPolicies().inNamespace("default").withName("foo").delete();
Tekton Client
Fabric8 Kubernetes Client also has an extension for Tekton. It is pretty much the same as Kubernetes Client but has support for some additional Tekton resources.
Initializing Tekton Client
Initializing Tekton client is the same as Kubernetes Client. You
try (final TektonClient client = new DefaultTektonClient()) {
// Do stuff with client
}
This would pick up default settings, reading your kubeconfig
file from ~/.kube/config
directory or whatever is defined inside KUBECONFIG
environment variable. But if you want to customize creation of client, you can also pass a Config
object inside DefaultTektonClient
. You can also create TektonClient
from an existing instance of KubernetesClient
. There is a method called adapt(..)
for this. Here is an example:
KubernetesClient client = new DefaultKubernetesClient();
TektonClient tektonClient = client.adapt(TektonClient.class);
Tekton Client DSL Usage
The Tekton client supports CRD API version tekton.dev/v1alpha1
as well as tekton.dev/v1beta1
. tekton.dev/v1alpha1
includes the CRDs Pipeline
, PipelineRun
, PipelineResource
, Task
, TaskRun
, Condition
and ClusterTask
. All tekton.dev/v1alpha1
resources are available using the DSL tektonClient.v1alpha1()
. tekton.dev/v1beta1
includes the CRDs Pipeline
, PipelineRun
, Task
, TaskRun
and ClusterTask
. All tekton.dev/v1beta1
resources are available using the DSL tektonClient.v1beta1()
. In addition to the Tekton Pipelines CRDs, the client also supports Tekton Triggers. TriggerTemplate
, TriggerBinding
, EventListener
and ClusterTriggerBinding
are available using the DSL tektonClient.v1alpha1()
.
The usage of the resources follows the same pattern as for K8s resources like Pods or Deployments. Here are some common examples:
- Listing all
PipelineRun
objects in some specific namespace:
PipelineRunList list = tektonClient.v1beta1().pipelineRuns().inNamespace("default").list();
- Create a
PipelineRun
:
PipelineRun pipelineRun = new PipelineRunBuilder()
.withNewMetadata().withName("demo-run-1").endMetadata()
.withNewSpec()
.withNewPipelineRef().withName("demo-pipeline").endPipelineRef()
.addNewParam().withName("greeting").withNewValue("Hello World!").endParam()
.endSpec()
.build();
tektonClient.v1beta1().pipelineRuns().inNamespace("default").create(pipelineRun);
Knative Client
Fabric8 Kubernetes Client also has an extension for Knative. It is pretty much the same as Kubernetes Client but has support for some additional Knative resources.
Initializing Knative Client
Initializing Knative client is the same as Kubernetes Client.
try (final KnativeClient client = new DefaultKnativeClient()) {
// Do stuff with client
}
This would pick up default settings, reading your kubeconfig
file from ~/.kube/config
directory or whatever is defined inside KUBECONFIG
environment variable. But if you want to customize creation of client, you can also pass a Config
object inside DefaultKnativeClient
. You can also create KnativeClient
from an existing instance of KubernetesClient
. There is a method called adapt(..)
for this. Here is an example:
KubernetesClient client = new DefaultKubernetesClient();
KnativeClient knativeClient = client.adapt(KnativeClient.class);
Knative Client DSL Usage
The usage of the resources follows the same pattern as for K8s resources like Pods or Deployments. Here are some common examples:
- Listing all
Service
objects in some specific namespace:
ServiceList list = knativeClient.services().inNamespace("default").list();
- Create a
Service
:
try (KnativeClient kn = new DefaultKnativeClient()) {
// Create Service object
Service service = new ServiceBuilder()
.withNewMetadata().withName("helloworld-go").endMetadata()
.withNewSpec()
.withNewTemplate()
.withNewSpec()
.addToContainers(new ContainerBuilder()
.withImage("gcr.io/knative-samples/helloworld-go")
.addNewEnv().withName("TARGET").withValue("Go Sample V1").endEnv()
.build())
.endSpec()
.endTemplate()
.endSpec()
.build();
// Apply it onto Kubernetes Server
kn.services().inNamespace("default").createOrReplace(service);
}