Skip to content

Commit ecc8dcf

Browse files
authored
Merge pull request #47560 from sheriff-rh/osdocs-2933.2
OSDOCS-2933 Java OSDK tutorial
2 parents ac18445 + d78476c commit ecc8dcf

19 files changed

+852
-39
lines changed

modules/osdk-bundle-operator.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Module included in the following assemblies:
22
//
33
// * operators/operator_sdk/golang/osdk-golang-tutorial.adoc
4+
// * operators/operator_sdk/java/osdk-java-tutorial.adoc
45
// * operators/operator_sdk/ansible/osdk-ansible-tutorial.adoc
56
// * operators/operator_sdk/helm/osdk-helm-tutorial.adoc
67
// * operators/operator_sdk/osdk-working-bundle-images.adoc
@@ -40,7 +41,7 @@ $ make docker-build IMG=<registry>/<user>/<operator_image_name>:<tag>
4041
+
4142
[NOTE]
4243
====
43-
The Dockerfile generated by the SDK for the Operator explicitly references `GOARCH=amd64` for `go build`. This can be amended to `GOARCH=$TARGETARCH` for non-AMD64 architectures. Docker will automatically set the environment variable to the value specified by `–platform`. With Buildah, the `–build-arg` will need to be used for the purpose. For more information, see link:https://sdk.operatorframework.io/docs/advanced-topics/multi-arch/#supporting-multiple-architectures[Multiple Architectures].
44+
The Dockerfile generated by the SDK for the Operator explicitly references `GOARCH=amd64` for `go build`. This can be amended to `GOARCH=$TARGETARCH` for non-AMD64 architectures. Docker will automatically set the environment variable to the value specified by `–platform`. With Buildah, the `–build-arg` will need to be used for the purpose. For more information, see link:https://sdk.operatorframework.io/docs/advanced-topics/multi-arch/#supporting-multiple-architectures[Multiple Architectures].
4445
====
4546

4647
.. Push the image to a repository:

modules/osdk-create-project.adoc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ ifeval::["{context}" == "osdk-helm-tutorial"]
1919
:type: Helm
2020
:app: nginx
2121
endif::[]
22+
ifeval::["{context}" == "osdk-java-tutorial"]
23+
:java:
24+
:type: Java
25+
:app: memcached
26+
endif::[]
2227

2328
:_content-type: PROCEDURE
2429
[id="osdk-create-project_{context}"]
@@ -58,6 +63,9 @@ endif::[]
5863
ifdef::helm[]
5964
with the `helm` plug-in
6065
endif::[]
66+
ifdef::java[]
67+
with the `quarkus` plug-in
68+
endif::[]
6169
to initialize the project:
6270
+
6371
[source,terminal,subs="attributes+"]
@@ -101,6 +109,14 @@ The `init` command creates the `nginx-operator` project specifically for watchin
101109

102110
. For Helm-based projects, the `init` command generates the RBAC rules in the `config/rbac/role.yaml` file based on the resources that would be deployed by the default manifest for the chart. Verify that the rules generated in this file meet the permission requirements of the Operator.
103111
endif::[]
112+
ifdef::java[]
113+
----
114+
$ operator-sdk init \
115+
--plugins=quarkus \
116+
--domain=example.com \
117+
--project-name=memcached-operator
118+
----
119+
endif::[]
104120

105121
ifeval::["{context}" == "osdk-golang-tutorial"]
106122
:!golang:
@@ -117,3 +133,8 @@ ifeval::["{context}" == "osdk-helm-tutorial"]
117133
:!type:
118134
:!app:
119135
endif::[]
136+
ifeval::["{context}" == "osdk-java-tutorial"]
137+
:!java:
138+
:!type:
139+
:!app:
140+
endif::[]

modules/osdk-deploy-olm.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ endif::[]
1111
ifeval::["{context}" == "osdk-working-bundle-images"]
1212
:golang:
1313
endif::[]
14+
ifeval::["{context}" == "osdk-java-tutorial"]
15+
:java:
16+
endif::[]
1417

1518
:_content-type: PROCEDURE
1619
[id="osdk-deploy-olm_{context}"]
@@ -64,3 +67,6 @@ endif::[]
6467
ifeval::["{context}" == "osdk-working-bundle-images"]
6568
:!golang:
6669
endif::[]
70+
ifeval::["{context}" == "osdk-java-tutorial"]
71+
:!java:
72+
endif::[]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Module included in the following assemblies:
2+
//
3+
// * operators/operator_sdk/java/osdk-java-tutorial.adoc
4+
5+
:_content-type: CONCEPT
6+
[id="osdk-java-controller-labels-memcached_{context}"]
7+
= Defining `labelsForMemcached`
8+
9+
`labelsForMemcached` is a utility to return a map of the labels to attach to the resources:
10+
11+
[source,java]
12+
----
13+
private Map<String, String> labelsForMemcached(Memcached m) {
14+
Map<String, String> labels = new HashMap<>();
15+
labels.put("app", "memcached");
16+
labels.put("memcached_cr", m.getMetadata().getName());
17+
return labels;
18+
}
19+
----
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Module included in the following assemblies:
2+
//
3+
// * operators/operator_sdk/java/osdk-java-tutorial.adoc
4+
5+
:_content-type: CONCEPT
6+
[id="osdk-java-controller-memcached-deployment_{context}"]
7+
= Define the `createMemcachedDeployment`
8+
9+
The `createMemcachedDeployment` method uses the link:https://fabric8.io/[fabric8] `DeploymentBuilder` class:
10+
11+
[source,java]
12+
----
13+
private Deployment createMemcachedDeployment(Memcached m) {
14+
return new DeploymentBuilder()
15+
.withMetadata(
16+
new ObjectMetaBuilder()
17+
.withName(m.getMetadata().getName())
18+
.withNamespace(m.getMetadata().getNamespace())
19+
.withOwnerReferences(
20+
new OwnerReferenceBuilder()
21+
.withApiVersion("v1")
22+
.withKind("Memcached")
23+
.withName(m.getMetadata().getName())
24+
.withUid(m.getMetadata().getUid())
25+
.build())
26+
.build())
27+
.withSpec(
28+
new DeploymentSpecBuilder()
29+
.withReplicas(m.getSpec().getSize())
30+
.withSelector(
31+
new LabelSelectorBuilder().withMatchLabels(labelsForMemcached(m)).build())
32+
.withTemplate(
33+
new PodTemplateSpecBuilder()
34+
.withMetadata(
35+
new ObjectMetaBuilder().withLabels(labelsForMemcached(m)).build())
36+
.withSpec(
37+
new PodSpecBuilder()
38+
.withContainers(
39+
new ContainerBuilder()
40+
.withImage("memcached:1.4.36-alpine")
41+
.withName("memcached")
42+
.withCommand("memcached", "-m=64", "-o", "modern", "-v")
43+
.withPorts(
44+
new ContainerPortBuilder()
45+
.withContainerPort(11211)
46+
.withName("memcached")
47+
.build())
48+
.build())
49+
.build())
50+
.build())
51+
.build())
52+
.build();
53+
}
54+
----
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Module included in the following assemblies:
2+
//
3+
// * operators/operator_sdk/java/osdk-java-tutorial.adoc
4+
5+
:_content-type: CONCEPT
6+
[id="osdk-java-controller-reconcile-loop_{context}"]
7+
= Reconcile loop
8+
9+
. Every controller has a reconciler object with a `Reconcile()` method that implements the reconcile loop. The reconcile loop is passed the `Deployment` argument, as shown in the following example:
10+
+
11+
[source,java]
12+
----
13+
Deployment deployment = client.apps()
14+
.deployments()
15+
.inNamespace(resource.getMetadata().getNamespace())
16+
.withName(resource.getMetadata().getName())
17+
.get();
18+
----
19+
20+
. As shown in the following example, if the `Deployment` is `null`, the deployment needs to be created. After you create the `Deployment`, you can determine if reconciliation is necessary. If there is no need of reconciliation, return the value of `UpdateControl.noUpdate()`, otherwise, return the value of `UpdateControl.updateStatus(resource):
21+
+
22+
[source, java]
23+
----
24+
if (deployment == null) {
25+
Deployment newDeployment = createMemcachedDeployment(resource);
26+
client.apps().deployments().create(newDeployment);
27+
return UpdateControl.noUpdate();
28+
}
29+
----
30+
31+
. After getting the `Deployment`, get the current and required replicas, as shown in the following example:
32+
+
33+
[source,java]
34+
----
35+
int currentReplicas = deployment.getSpec().getReplicas();
36+
int requiredReplicas = resource.getSpec().getSize();
37+
----
38+
39+
. If `currentReplicas` does not match the `requiredReplicas`, you must update the `Deployment`, as shown in the following example:
40+
+
41+
[source,java]
42+
----
43+
if (currentReplicas != requiredReplicas) {
44+
deployment.getSpec().setReplicas(requiredReplicas);
45+
client.apps().deployments().createOrReplace(deployment);
46+
return UpdateControl.noUpdate();
47+
}
48+
----
49+
50+
. The following example shows how to obtain the list of pods and their names:
51+
+
52+
[source,java]
53+
----
54+
List<Pod> pods = client.pods()
55+
.inNamespace(resource.getMetadata().getNamespace())
56+
.withLabels(labelsForMemcached(resource))
57+
.list()
58+
.getItems();
59+
60+
List<String> podNames =
61+
pods.stream().map(p -> p.getMetadata().getName()).collect(Collectors.toList());
62+
----
63+
64+
. Check if resources were created and verify podnames with the Memcached resources. If a mismatch exists in either of these conditions, perform a reconciliation as shown in the following example:
65+
+
66+
[source,java]
67+
----
68+
if (resource.getStatus() == null
69+
|| !CollectionUtils.isEqualCollection(podNames, resource.getStatus().getNodes())) {
70+
if (resource.getStatus() == null) resource.setStatus(new MemcachedStatus());
71+
resource.getStatus().setNodes(podNames);
72+
return UpdateControl.updateResource(resource);
73+
}
74+
----
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Module included in the following assemblies:
2+
//
3+
// * operators/operator_sdk/java/osdk-java-tutorial.adoc
4+
5+
:_content-type: PROCEDURE
6+
[id="osdk-java-create-api-controller_{context}"]
7+
= Creating an API and controller
8+
9+
Use the Operator SDK CLI to create a custom resource definition (CRD) API and controller.
10+
11+
.Procedure
12+
13+
. Run the following command to create an API:
14+
+
15+
[source,terminal]
16+
----
17+
$ operator-sdk create api \
18+
--plugins=quarkus \ <1>
19+
--group=cache \ <2>
20+
--version=v1 \ <3>
21+
--kind=Memcached <4>
22+
----
23+
<1> Set the plug-in flag to `quarkus`.
24+
<2> Set the group flag to `cache`.
25+
<3> Set the version flag to `v1`.
26+
<4> Set the kind flag to `Memcached`.
27+
28+
.Verification
29+
30+
. Run the `tree` command to view the file structure:
31+
+
32+
[source,terminal]
33+
----
34+
$ tree
35+
----
36+
+
37+
.Example output
38+
[source,terminal]
39+
----
40+
.
41+
├── Makefile
42+
├── PROJECT
43+
├── pom.xml
44+
└── src
45+
└── main
46+
├── java
47+
│ └── com
48+
│ └── example
49+
│ ├── Memcached.java
50+
│ ├── MemcachedReconciler.java
51+
│ ├── MemcachedSpec.java
52+
│ └── MemcachedStatus.java
53+
└── resources
54+
└── application.properties
55+
56+
6 directories, 8 files
57+
----

modules/osdk-java-create-cr.adoc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Module included in the following assemblies:
2+
//
3+
// * operators/operator_sdk/java/osdk-java-tutorial.adoc
4+
5+
:_content-type: PROCEDURE
6+
[id="osdk-java-create-cr_{context}"]
7+
= Creating a Custom Resource
8+
9+
After generating the CRD manifests, you can create the Custom Resource (CR).
10+
11+
.Procedure
12+
* Create a Memcached CR called `memcached-sample.yaml`:
13+
+
14+
[source,yaml]
15+
----
16+
apiVersion: cache.example.com/v1
17+
kind: Memcached
18+
metadata:
19+
name: memcached-sample
20+
spec:
21+
# Add spec fields here
22+
size: 1
23+
----

modules/osdk-java-define-api.adoc

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Module included in the following assemblies:
2+
//
3+
// * operators/operator_sdk/java/osdk-java-tutorial.adoc
4+
5+
:_content-type: PROCEDURE
6+
[id="osdk-java-define-api_{context}"]
7+
= Defining the API
8+
9+
Define the API for the `Memcached` custom resource (CR).
10+
11+
.Procedure
12+
* Edit the following files that were generated as part of the `create api` process:
13+
14+
.. Update the following attributes in the `MemcachedSpec.java` file to define the desired state of the `Memcached` CR:
15+
+
16+
[source,java]
17+
----
18+
public class MemcachedSpec {
19+
20+
private Integer size;
21+
22+
public Integer getSize() {
23+
return size;
24+
}
25+
26+
public void setSize(Integer size) {
27+
this.size = size;
28+
}
29+
}
30+
----
31+
32+
.. Update the following attributes in the `MemcachedStatus.java` file to define the observed state of the `Memcached` CR:
33+
+
34+
[NOTE]
35+
====
36+
The example below illustrates a Node status field. It is recommended that you use link:https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties[typical status properties] in practice.
37+
====
38+
+
39+
[source,java]
40+
----
41+
import java.util.ArrayList;
42+
import java.util.List;
43+
44+
public class MemcachedStatus {
45+
46+
// Add Status information here
47+
// Nodes are the names of the memcached pods
48+
private List<String> nodes;
49+
50+
public List<String> getNodes() {
51+
if (nodes == null) {
52+
nodes = new ArrayList<>();
53+
}
54+
return nodes;
55+
}
56+
57+
public void setNodes(List<String> nodes) {
58+
this.nodes = nodes;
59+
}
60+
}
61+
----
62+
63+
.. Update the `Memcached.java` file to define the Schema for Memcached APIs that extends to both `MemcachedSpec.java` and `MemcachedStatus.java` files.
64+
+
65+
[source,java]
66+
----
67+
@Version("v1")
68+
@Group("cache.example.com")
69+
public class Memcached extends CustomResource<MemcachedSpec, MemcachedStatus> implements Namespaced {}
70+
----

0 commit comments

Comments
 (0)