Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #1868: Add Support for rolling update #2272

Merged
merged 1 commit into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#### Dependency Upgrade

#### New Features
* Fix #1868: Add Support for rolling update

### 4.10.2 (2020-06-02)
#### Bugs
Expand Down
199 changes: 190 additions & 9 deletions doc/CHEATSHEET.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ This document contains common usages of different resources using Fabric8 Kubern
* [Initializing Tekton Client](#initializing-tekton-client)
* [Tekton Client DSL Usage](#tekton-client-dsl-usage)

* [Knative Client](#knative-client)
* [Initializing Knative Client](#initializing-knative-client)
* [Knative Client DSL Usage](#knative-client-dsl-usage)

### Initializing Kubernetes Client
Typically, we create Kubernetes Client like this:
```
Expand Down Expand Up @@ -343,18 +347,54 @@ Deployment updatedDeploy = client.apps().deployments().inNamespace("default")
.withName("deployment1").edit()
.editSpec().withReplicas(2).endSpec().done();
```
- Rolling update a `Deployment`:
- Update single container image inside `Deployment`:
```
// Not sure about state of this, but this way should work. Otherwise it's a bug
client.apps().deployments().inNamespace("default").withName("ngix-controller")
Deployment updatedDeployment = client.apps().deployments().inNamespace("default").withName("ngix-controller")
.rolling().updateImage("docker.io/nginx:latest");
```
- Update multiple container images inside `Deployment`:
```
Map<String, String> containerToImageMap = new HashMap<>();
containerToImageMap.put("nginx", "nginx:perl");
containerToImageMap.put("sidecar", "someImage:someVersion");
Deployment updatedDeployment = client.apps().deployments()
.inNamespace("default")
.withName("nginx-deployment")
.rolling()
.updateImage(containerToImageMap);

// Workaround specified in https://github.com/fabric8io/kubernetes-client/issues/1868#issuecomment-569908949
client.apps().deployments().inNamespace("default").withName("nginx-controller")
.edit().editSpec().editTemplate().editMetadata()
.addToAnnotations("dummy", "du_"+System.currentTimeMillis())
.endMetadata().endTemplate().endSpec()
.done();
```
- Rollout restart a `Deployment`:
```
Deployment deployment = client.apps().deployments()
.inNamespace("default")
.withName("nginx-deployment")
.rolling()
.restart();
```
- Pause Rollout of a `Deployment`:
```
Deployment deployment = client.apps().deployments()
.inNamespace("default")
.withName("nginx-deployment")
.rolling()
.pause();
```
- Resume Rollout of a `Deployment`:
```
Deployment deployment = client.apps().deployments()
.inNamespace("default")
.withName("nginx-deployment")
.rolling()
.resume();
```
- Undo Rollout of a `Deployment`:
```
Deployment deployment = client.apps().deployments()
.inNamespace("default")
.withName("nginx-deployment")
.rolling()
.undo();
```
- Deleting a `Deployment`:
```
Expand Down Expand Up @@ -502,6 +542,25 @@ client.apps().replicaSets().inNamespace("default").watch(new Watcher<ReplicaSet>
// Scale to 3 replicas
client.apps().replicaSets().inNamespace("default").withName("nginx-rs").scale(3);
```
- Update Image in `ReplicaSet`
```
ReplicaSet replicaSet = client.apps().replicaSets()
.inNamespace("default")
.withName("soaktestrs")
.rolling()
.updateImage("nickchase/soaktest");
```
- Update multiple Images in `ReplicaSet`:
```
Map<String, String> containerToImageMap = new HashMap<>();
containerToImageMap.put("c1", "image1");
containerToImageMap.put("c2", "image2");
ReplicaSet replicaSet = client.apps().replicaSets()
.inNamespace("default")
.withName("soaktestrs")
.rolling()
.updateImage(containerToImageMap);
```

### ReplicationController

Expand Down Expand Up @@ -585,6 +644,25 @@ client.replicationControllers().inNamespace(currentNamespace).watch(new Watcher<
```
ReplicationController rc = client.replicationControllers().inNamespace("default").withName("nginx-controller").scale(2);
```
- Update image in `ReplicationController`:
```
ReplicationController rc = client.replicationControllers()
.inNamespace("default")
.withName("nginx")
.rolling()
.updateImage("nginx:latest");
```
- Update multiple images in `ReplicationController`:
```
Map<String, String> containerToImageMap = new HashMap<>();
containerToImageMap.put("c1", "image1");
containerToImageMap.put("c2", "image2");
ReplicationController rc = client.replicationControllers()
.inNamespace("default")
.withName("nginx")
.rolling()
.updateImage(controllerToImageMap);
```

### ConfigMap
`ConfigMap` resource is available in Kubernetes Client api via the `client.configMaps()`. Here are some examples of common usage:
Expand Down Expand Up @@ -1141,6 +1219,57 @@ client.apps().statefulSets().inNamespace("default").withName("ss1").watch(new Wa
}
})
```
- Update Image in `StatefulSet`:
```
StatefulSet statefulSet = client.apps().statefulSets()
.inNamespace("default")
.withName("web")
.rolling()
.updateImage("nginx:1.19");
```
- Updated multiple containers in `StatefulSet`:
```
Map<String, String> containerToImageMap = new HashMap<>();
containerToImageMap("container1", "nginx:1.9");
containerToImageMap("container2", "busybox:latest");
Statefulset statefulSet = client.apps().statefulSets()
.inNamespace("default")
.withName("web")
.rolling()
.updateImage(params);
```
- Restart Rollout for `StatefulSet`:
```
StatefulSet ss = client.apps().statefulSets()
.inNamespace("default")
.withName("web")
.rolling()
.restart();
```
- Pause Rollout for `StatefulSet`:
```
StatefulSet ss = client.apps().statefulSets()
.inNamespace("default")
.withName("web")
.rolling()
.pause();
```
- Resume Rollout for `StatefulSet`:
```
StatefulSet ss = client.apps().statefulSets()
.inNamespace("default")
.withName("web")
.rolling()
.resume();
```
- Undo Rollout for `StatefulSet`:
```
StatefulSet ss = client.apps().statefulSets()
.inNamespace("default")
.withName("web")
.rolling()
.undo();
```

### DaemonSet
`DaemonSet` resource is available in Kubernetes Client API via `client.apps().daemonSets()`. Here are some examples of its common usage:
Expand Down Expand Up @@ -2353,3 +2482,55 @@ PipelineRun pipelineRun = new PipelineRunBuilder()

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);
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,53 @@
*/
package io.fabric8.kubernetes.client.dsl;

import java.util.Map;

public interface ImageEditReplacePatchable<I, T, D> extends EditReplacePatchable<I, T, D> {

/**
* Update existing container image(s) of resources
*
* @param containerToImageMap Map with keys as container name and value as image
* @return updated resource
*/
T updateImage(Map<String, String> containerToImageMap);

/**
* Update existing container image of single container resource
*
* @param image image to be updated
* @return updated resource
*/
T updateImage(String image);

/**
* Mark the provided resource as paused
*
* @return updated resource
*/
T pause();

/**
* Resume a paused resource. Paused resources will not be reconciled by a controller.
* By resuming a resource, we allow it to be reconciled again.
*
* @return updated resource
*/
T resume();

/**
* Restart a resource. Resource will be rollout restarted.
*
* @return updated resource
*/
T restart();

/**
* Rollback to previous rollout.
*
* @return updated resource
*/
T undo();

}
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,22 @@ protected T handlePatch(T current, T updated) throws ExecutionException, Interru
return handlePatch(current, updated, getType());
}

protected T handlePatch(T current, Map<String, Object> patchedUpdate) throws ExecutionException, InterruptedException, IOException {
updateApiVersionResource(current);
return handlePatch(current, patchedUpdate, getType());
}

protected T sendPatchedObject(T oldObject, T updatedObject) {
try {
return handlePatch(oldObject, updatedObject);
} catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
throw KubernetesClientException.launderThrowable(interruptedException);
} catch (ExecutionException | IOException e) {
throw KubernetesClientException.launderThrowable(e);
}
}

protected Scale handleScale(Scale scaleParam) {
try {
return handleScale(getCompleteResourceUrl().toString(), scaleParam);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class OperationSupport {

public static final MediaType JSON = MediaType.parse("application/json");
public static final MediaType JSON_PATCH = MediaType.parse("application/json-patch+json");
public static final MediaType STRATEGIC_MERGE_JSON_PATCH = MediaType.parse("application/strategic-merge-patch+json");
protected static final ObjectMapper JSON_MAPPER = Serialization.jsonMapper();
protected static final ObjectMapper YAML_MAPPER = Serialization.yamlMapper();
private static final String CLIENT_STATUS_FLAG = "CLIENT_STATUS_FLAG";
Expand Down Expand Up @@ -316,6 +317,26 @@ protected <T> T handlePatch(T current, T updated, Class<T> type) throws Executio
return handleResponse(requestBuilder, type, Collections.<String, String>emptyMap());
}

/**
* Send an http patch and handle the response.
*
* @param current current object
* @param patchForUpdate updated object spec as json string
* @param type type of object
* @param <T> template argument provided
*
* @return returns de-serialized version of api server response
* @throws ExecutionException Execution Exception
* @throws InterruptedException Interrupted Exception
* @throws KubernetesClientException KubernetesClientException
* @throws IOException IOException
*/
protected <T> T handlePatch(T current, Map<String, Object> patchForUpdate, Class<T> type) throws ExecutionException, InterruptedException, IOException {
RequestBody body = RequestBody.create(STRATEGIC_MERGE_JSON_PATCH, JSON_MAPPER.writeValueAsString(patchForUpdate));
Request.Builder requestBuilder = new Request.Builder().patch(body).url(getResourceUrl(checkNamespace(current), checkName(current)));
return handleResponse(requestBuilder, type, Collections.<String, String>emptyMap());
}

/**
* Replace Scale of specified Kubernetes Resource
*
Expand Down Expand Up @@ -589,6 +610,10 @@ protected static <T> T unmarshal(InputStream is, TypeReference<T> type) throws K
return Serialization.unmarshal(is, type);
}

protected static <T> Map getObjectValueAsMap(T object) {
return JSON_MAPPER.convertValue(object, Map.class);
}

public Config getConfig() {
return config;
}
Expand Down
Loading