diff --git a/CHANGELOG.md b/CHANGELOG.md index 198c7224a45..461d5edb047 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ #### Improvements * Fix #2473: Removed unused ValidationMessages.properties * Fix #2408: Add documentation for Pod log options +* Fix #2141: Decouple OpenShift Model from Kubernetes Client #### Dependency Upgrade * Bump Knative Serving to v0.17.2 & Knative Eventing to v0.17.3 diff --git a/kubernetes-client/pom.xml b/kubernetes-client/pom.xml index dc0b1208cbd..e6b0111521b 100644 --- a/kubernetes-client/pom.xml +++ b/kubernetes-client/pom.xml @@ -130,10 +130,6 @@ io.fabric8 kubernetes-model-storageclass - - io.fabric8 - openshift-model - com.squareup.okhttp3 okhttp diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/VersionInfo.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/VersionInfo.java index 97b1935c2ff..0ecc65afda2 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/VersionInfo.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/VersionInfo.java @@ -16,14 +16,13 @@ package io.fabric8.kubernetes.client; -import io.fabric8.openshift.api.model.ClusterVersion; - import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class VersionInfo { - public final class VERSION_KEYS { + public static final class VersionKeys { + private VersionKeys() {} public static final String BUILD_DATE = "buildDate"; public static final String GIT_COMMIT = "gitCommit"; public static final String GIT_VERSION = "gitVersion"; @@ -83,17 +82,6 @@ public String getCompiler() { private VersionInfo() { } - public static VersionInfo parseVersionInfoFromClusterVersion(ClusterVersion clusterVersion) throws ParseException { - String[] versionParts = clusterVersion.getStatus().getDesired().getVersion().split("\\."); - VersionInfo.Builder versionInfoBuilder = new VersionInfo.Builder(); - if (versionParts.length == 3) { - versionInfoBuilder.withMajor(versionParts[0]); - versionInfoBuilder.withMinor(versionParts[1] + "." + versionParts[2]); - } - versionInfoBuilder.withBuildDate(clusterVersion.getMetadata().getCreationTimestamp()); - return versionInfoBuilder.build(); - } - public static class Builder { private VersionInfo versionInfo = new VersionInfo(); @@ -114,7 +102,7 @@ public Builder(VersionInfo versionInfo) { } public Builder withBuildDate(String buildDate) throws ParseException { - this.versionInfo.buildDate = new SimpleDateFormat(VERSION_KEYS.BUILD_DATE_FORMAT).parse(buildDate); + this.versionInfo.buildDate = new SimpleDateFormat(VersionKeys.BUILD_DATE_FORMAT).parse(buildDate); return this; } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ClusterOperationsImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ClusterOperationsImpl.java index 6bbc85a43bf..6063fb6cded 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ClusterOperationsImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ClusterOperationsImpl.java @@ -16,31 +16,25 @@ package io.fabric8.kubernetes.client.dsl.internal; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.fabric8.kubernetes.client.Version; import io.fabric8.kubernetes.client.VersionInfo; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.dsl.base.OperationContext; import io.fabric8.kubernetes.client.dsl.base.OperationSupport; +import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.kubernetes.client.utils.URLUtils; -import io.fabric8.openshift.api.model.ClusterVersionList; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import java.io.IOException; -import java.net.HttpURLConnection; import java.text.ParseException; import java.util.HashMap; import java.util.Map; public class ClusterOperationsImpl extends OperationSupport { - private String versionEndpoint; + protected final String versionEndpoint; public static final String KUBERNETES_VERSION_ENDPOINT = "version"; - public static final String OPENSHIFT_VERSION_ENDPOINT = "version/openshift"; - public static final String OPENSHIFT4_VERSION_ENDPOINT = "apis/config.openshift.io/v1/clusterversions"; - public static final ObjectMapper objectMapper = new ObjectMapper(); public ClusterOperationsImpl(OkHttpClient client, Config config, String item) { super(new OperationContext().withOkhttpClient(client).withConfig(config)); @@ -50,48 +44,34 @@ public ClusterOperationsImpl(OkHttpClient client, Config config, String item) { public VersionInfo fetchVersion() { try { Response response = handleVersionGet(versionEndpoint); - // Handle Openshift 4 version case - if (HttpURLConnection.HTTP_NOT_FOUND == response.code() && versionEndpoint.equals(OPENSHIFT_VERSION_ENDPOINT)) { - response.close(); - return fetchOpenshift4Version(); - } + Map myMap = new HashMap<>(); - Map myMap = objectMapper.readValue(response.body().string(), HashMap.class); + if (response.body() != null) { + myMap = Serialization.jsonMapper().readValue(response.body().string(), HashMap.class); + } return fetchVersionInfoFromResponse(myMap); } catch(Exception e) { - KubernetesClientException.launderThrowable(e); + throw KubernetesClientException.launderThrowable(e); } - return null; } - private Response handleVersionGet(String versionEndpointToBeUsed) throws IOException { + protected Response handleVersionGet(String versionEndpointToBeUsed) throws IOException { Request.Builder requestBuilder = new Request.Builder() .get() .url(URLUtils.join(config.getMasterUrl(), versionEndpointToBeUsed)); return client.newCall(requestBuilder.build()).execute(); } - private VersionInfo fetchOpenshift4Version() throws IOException, ParseException { - Response response = handleVersionGet(OPENSHIFT4_VERSION_ENDPOINT); - if (response.isSuccessful() && response.body() != null) { - ClusterVersionList clusterVersionList = objectMapper.readValue(response.body().string(), ClusterVersionList.class); - if (!clusterVersionList.getItems().isEmpty()) { - return VersionInfo.parseVersionInfoFromClusterVersion(clusterVersionList.getItems().get(0)); - } - } - return null; - } - - private VersionInfo fetchVersionInfoFromResponse(Map responseAsMap) throws ParseException { - return new VersionInfo.Builder().withBuildDate(responseAsMap.get(VersionInfo.VERSION_KEYS.BUILD_DATE)) - .withGitCommit(responseAsMap.get(VersionInfo.VERSION_KEYS.GIT_COMMIT)) - .withGitVersion(responseAsMap.get(VersionInfo.VERSION_KEYS.GIT_VERSION)) - .withMajor(responseAsMap.get(VersionInfo.VERSION_KEYS.MAJOR)) - .withMinor(responseAsMap.get(VersionInfo.VERSION_KEYS.MINOR)) - .withGitTreeState(responseAsMap.get(VersionInfo.VERSION_KEYS.GIT_TREE_STATE)) - .withPlatform(responseAsMap.get(VersionInfo.VERSION_KEYS.PLATFORM)) - .withGoVersion(responseAsMap.get(VersionInfo.VERSION_KEYS.GO_VERSION)) - .withCompiler(responseAsMap.get(VersionInfo.VERSION_KEYS.COMPILER)) + protected static VersionInfo fetchVersionInfoFromResponse(Map responseAsMap) throws ParseException { + return new VersionInfo.Builder().withBuildDate(responseAsMap.get(VersionInfo.VersionKeys.BUILD_DATE)) + .withGitCommit(responseAsMap.get(VersionInfo.VersionKeys.GIT_COMMIT)) + .withGitVersion(responseAsMap.get(VersionInfo.VersionKeys.GIT_VERSION)) + .withMajor(responseAsMap.get(VersionInfo.VersionKeys.MAJOR)) + .withMinor(responseAsMap.get(VersionInfo.VersionKeys.MINOR)) + .withGitTreeState(responseAsMap.get(VersionInfo.VersionKeys.GIT_TREE_STATE)) + .withPlatform(responseAsMap.get(VersionInfo.VersionKeys.PLATFORM)) + .withGoVersion(responseAsMap.get(VersionInfo.VersionKeys.GO_VERSION)) + .withCompiler(responseAsMap.get(VersionInfo.VersionKeys.COMPILER)) .build(); } } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java index 51406545368..683ddc67ea3 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java @@ -16,14 +16,12 @@ package io.fabric8.kubernetes.client.dsl.internal; import com.fasterxml.jackson.databind.ObjectMapper; -import com.mifmif.common.regex.Generex; import io.fabric8.kubernetes.api.builder.TypedVisitor; import io.fabric8.kubernetes.api.builder.VisitableBuilder; import io.fabric8.kubernetes.api.builder.Visitor; import io.fabric8.kubernetes.api.model.DeletionPropagation; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.KubernetesListBuilder; import io.fabric8.kubernetes.api.model.KubernetesResourceList; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.Config; @@ -39,8 +37,6 @@ import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.kubernetes.client.utils.Utils; -import io.fabric8.openshift.api.model.Parameter; -import io.fabric8.openshift.api.model.Template; import java.net.HttpURLConnection; import java.util.function.Predicate; @@ -67,8 +63,8 @@ public class NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImp Waitable, HasMetadata>, Readiable { private static final Logger LOGGER = LoggerFactory.getLogger(NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.class); - private static final String EXPRESSION = "expression"; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + protected static final String EXPRESSION = "expression"; + protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final String fallbackNamespace; private final String explicitNamespace; @@ -181,7 +177,7 @@ public List waitUntilCondition(Predicate condition, lo @Override public Boolean isReady() { for (final HasMetadata meta : acceptVisitors(get(), visitors)) { - if (!Readiness.isReady(meta)) { + if (!isResourceReady(meta)) { return false; } } @@ -394,17 +390,14 @@ public Deletable cascading(boolean cascading) { return new NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl(client, config, fallbackNamespace, explicitNamespace, fromServer, true, visitors, item, null, null, gracePeriodSeconds, propagationPolicy, cascading, watchRetryInitialBackoffMillis, watchRetryBackoffMultiplier); } - private static List asHasMetadata(T item, Boolean enableProccessing) { + protected boolean isResourceReady(HasMetadata meta) { + return Readiness.isReady(meta); + } + + protected List asHasMetadata(T item, Boolean enableProccessing) { List result = new ArrayList<>(); if (item instanceof KubernetesList) { result.addAll(((KubernetesList) item).getItems()); - } else if (item instanceof Template) { - - if (!enableProccessing) { - result.addAll(((Template) item).getObjects()); - } else { - result.addAll(processTemplate((Template)item, false)); - } } else if (item instanceof KubernetesResourceList) { result.addAll(((KubernetesResourceList) item).getItems()); } else if (item instanceof HasMetadata) { @@ -435,43 +428,6 @@ private static ResourceHandler handlerOf(T item) { } } - private static List processTemplate(Template template, Boolean failOnMissing) { - List parameters = template != null ? template.getParameters() : null; - KubernetesList list = new KubernetesListBuilder() - .withItems(template.getObjects()) - .build(); - - try { - String json = OBJECT_MAPPER.writeValueAsString(list); - if (parameters != null && !parameters.isEmpty()) { - // lets make a few passes in case there's expressions in values - for (int i = 0; i < 5; i++) { - for (Parameter parameter : parameters) { - String name = parameter.getName(); - String regex = "${" + name + "}"; - String value; - if (Utils.isNotNullOrEmpty(parameter.getValue())) { - value = parameter.getValue(); - } else if (EXPRESSION.equals(parameter.getGenerate())) { - Generex generex = new Generex(parameter.getFrom()); - value = generex.random(); - } else if (failOnMissing) { - throw new IllegalArgumentException("No value available for parameter name: " + name); - } else { - value = ""; - } - json = json.replace(regex, value); - } - } - } - - list = OBJECT_MAPPER.readValue(json, KubernetesList.class); - } catch (IOException e) { - throw KubernetesClientException.launderThrowable(e); - } - return list.getItems(); - } - /** * Waits until the latch reaches to zero and then checks if the expected result * @param latch The latch. diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/PatchUtils.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/PatchUtils.java index 1f792f35b0e..1cc6483fcec 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/PatchUtils.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/PatchUtils.java @@ -18,22 +18,25 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import io.fabric8.kubernetes.api.model.ObjectMeta; -import io.fabric8.kubernetes.client.internal.patchmixins.BuildMixIn; import io.fabric8.kubernetes.client.internal.patchmixins.ObjectMetaMixIn; -import io.fabric8.openshift.api.model.Build; public class PatchUtils { + private PatchUtils() { } + private static class SingletonHolder { public static final ObjectMapper patchMapper; static { patchMapper = new ObjectMapper(); patchMapper.addMixIn(ObjectMeta.class, ObjectMetaMixIn.class); - patchMapper.addMixIn(Build.class, BuildMixIn.class); patchMapper.setConfig(patchMapper.getSerializationConfig().without(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS)); } } + public static void addMixInToMapper(Class target, Class mixInSource) { + SingletonHolder.patchMapper.addMixIn(target, mixInSource); + } + public static ObjectMapper patchMapper() { return SingletonHolder.patchMapper; } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/readiness/Readiness.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/readiness/Readiness.java index 059e42c1fb8..4a271e0310b 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/readiness/Readiness.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/readiness/Readiness.java @@ -35,9 +35,6 @@ import io.fabric8.kubernetes.api.model.apps.StatefulSetSpec; import io.fabric8.kubernetes.api.model.apps.StatefulSetStatus; import io.fabric8.kubernetes.client.utils.Utils; -import io.fabric8.openshift.api.model.DeploymentConfig; -import io.fabric8.openshift.api.model.DeploymentConfigSpec; -import io.fabric8.openshift.api.model.DeploymentConfigStatus; public class Readiness { @@ -51,7 +48,6 @@ public static boolean isReadinessApplicable(Class itemCla || io.fabric8.kubernetes.api.model.extensions.Deployment.class.isAssignableFrom(itemClass) || ReplicaSet.class.isAssignableFrom(itemClass) || Pod.class.isAssignableFrom(itemClass) - || DeploymentConfig.class.isAssignableFrom(itemClass) || ReplicationController.class.isAssignableFrom(itemClass) || Endpoints.class.isAssignableFrom(itemClass) || Node.class.isAssignableFrom(itemClass) @@ -60,6 +56,14 @@ public static boolean isReadinessApplicable(Class itemCla } public static boolean isReady(HasMetadata item) { + if (isReadiableKubernetesResource(item)) { + return isKubernetesResourceReady(item); + } else { + throw new IllegalArgumentException("Item needs to be one of [Node, Deployment, ReplicaSet, StatefulSet, Pod, ReplicationController], but was: [" + (item != null ? item.getKind() : "Unknown (null)") + "]"); + } + } + + private static boolean isKubernetesResourceReady(HasMetadata item) { if (item instanceof Deployment) { return isDeploymentReady((Deployment) item); } else if (item instanceof io.fabric8.kubernetes.api.model.extensions.Deployment) { @@ -68,8 +72,6 @@ public static boolean isReady(HasMetadata item) { return isReplicaSetReady((ReplicaSet) item); } else if (item instanceof Pod) { return isPodReady((Pod) item); - } else if (item instanceof DeploymentConfig) { - return isDeploymentConfigReady((DeploymentConfig) item); } else if (item instanceof ReplicationController) { return isReplicationControllerReady((ReplicationController) item); } else if (item instanceof Endpoints) { @@ -78,9 +80,8 @@ public static boolean isReady(HasMetadata item) { return isNodeReady((Node) item); } else if (item instanceof StatefulSet) { return isStatefulSetReady((StatefulSet) item); - } else { - throw new IllegalArgumentException("Item needs to be one of [Node, Deployment, ReplicaSet, StatefulSet, Pod, DeploymentConfig, ReplicationController], but was: [" + (item != null ? item.getKind() : "Unknown (null)") + "]"); } + return false; } public static boolean isStatefulSetReady(StatefulSet ss) { @@ -157,24 +158,6 @@ public static boolean isReplicaSetReady(ReplicaSet r) { } - public static boolean isDeploymentConfigReady(DeploymentConfig d) { - Utils.checkNotNull(d, "Deployment can't be null."); - DeploymentConfigSpec spec = d.getSpec(); - DeploymentConfigStatus status = d.getStatus(); - - if (status == null || status.getReplicas() == null || status.getAvailableReplicas() == null) { - return false; - } - - //Can be true in testing, so handle it to make test writing easier. - if (spec == null || spec.getReplicas() == null) { - return false; - } - - return spec.getReplicas().intValue() == status.getReplicas() && - spec.getReplicas().intValue() <= status.getAvailableReplicas(); - } - public static boolean isReplicationControllerReady(ReplicationController r) { Utils.checkNotNull(r, "ReplicationController can't be null."); ReplicationControllerSpec spec = r.getSpec(); @@ -270,6 +253,17 @@ private static NodeCondition getNodeReadyCondition(Node node) { } return null; } + + protected static boolean isReadiableKubernetesResource(HasMetadata item) { + return (item instanceof Deployment || + item instanceof io.fabric8.kubernetes.api.model.extensions.Deployment || + item instanceof ReplicaSet || + item instanceof Pod || + item instanceof ReplicationController || + item instanceof Endpoints || + item instanceof Node || + item instanceof StatefulSet); + } } diff --git a/kubernetes-itests/src/test/java/io/fabric8/openshift/DeploymentConfigIT.java b/kubernetes-itests/src/test/java/io/fabric8/openshift/DeploymentConfigIT.java index 6f392e36f9f..41c5b5b015b 100644 --- a/kubernetes-itests/src/test/java/io/fabric8/openshift/DeploymentConfigIT.java +++ b/kubernetes-itests/src/test/java/io/fabric8/openshift/DeploymentConfigIT.java @@ -89,6 +89,14 @@ public void delete() { assertTrue(bDeleted); } + @Test + public void testWaitUntilReady() throws InterruptedException { + DeploymentConfig deploymentConfig = client.deploymentConfigs().inNamespace(session.getNamespace()).withName("dc-waituntilready").waitUntilReady(2, TimeUnit.MINUTES); + assertNotNull(deploymentConfig); + assertEquals(1, deploymentConfig.getStatus().getAvailableReplicas().intValue()); + assertTrue(deploymentConfig.getStatus().getConditions().stream().anyMatch(c -> c.getType().equals("Available"))); + } + @Test public void createOrReplace() { // Given diff --git a/kubernetes-itests/src/test/resources/deploymentconfig-it.yml b/kubernetes-itests/src/test/resources/deploymentconfig-it.yml index 62d98990b65..654beb082e6 100644 --- a/kubernetes-itests/src/test/resources/deploymentconfig-it.yml +++ b/kubernetes-itests/src/test/resources/deploymentconfig-it.yml @@ -31,7 +31,7 @@ spec: ports: - containerPort: 8080 protocol: "TCP" - replicas: 5 + replicas: 1 selector: name: "frontend" triggers: @@ -63,7 +63,7 @@ spec: ports: - containerPort: 8080 protocol: "TCP" - replicas: 5 + replicas: 1 selector: name: "frontend" triggers: @@ -95,7 +95,7 @@ spec: ports: - containerPort: 8080 protocol: "TCP" - replicas: 5 + replicas: 1 selector: name: "frontend" triggers: @@ -127,7 +127,7 @@ spec: ports: - containerPort: 8080 protocol: "TCP" - replicas: 5 + replicas: 1 selector: name: "frontend" triggers: @@ -159,7 +159,7 @@ spec: ports: - containerPort: 8080 protocol: "TCP" - replicas: 5 + replicas: 1 selector: name: "frontend" triggers: @@ -174,3 +174,25 @@ spec: name: "origin-ruby-sample:latest" strategy: type: "Rolling" +--- +kind: "DeploymentConfig" +apiVersion: "v1" +metadata: + name: dc-waituntilready +spec: + template: + metadata: + labels: + name: "frontend" + spec: + containers: + - name: "helloworld" + image: "openshift/hello-openshift:latest" + ports: + - containerPort: 8080 + protocol: "TCP" + replicas: 1 + selector: + name: "frontend" + triggers: + - type: ConfigChange diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LoadTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LoadTest.java index ddb0b778146..98f3b93f72a 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LoadTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LoadTest.java @@ -30,13 +30,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; @EnableRuleMigrationSupport -public class LoadTest { +class LoadTest { @Rule public KubernetesServer server = new KubernetesServer(); @Test - public void testResourceGetFromLoadWhenMultipleDocumentsWithDelimiter() throws Exception { + void testResourceGetFromLoadWhenMultipleDocumentsWithDelimiter() throws Exception { // given KubernetesClient client = server.getClient(); @@ -52,40 +52,6 @@ public void testResourceGetFromLoadWhenMultipleDocumentsWithDelimiter() throws E assertEquals("redis-master", deploymentResource.getMetadata().getName()); } - @Test - public void testResourceGetFromLoadWhenSingleDocumentsWithoutDelimiter() throws Exception { - // given - KubernetesClient client = server.getClient(); - - // when - List result = client.load(getClass().getResourceAsStream("/template-with-params.yml")).get(); - - // then - assertNotNull(result); - assertEquals(1, result.size()); - HasMetadata deploymentResource = result.get(0); - assertEquals("v1", deploymentResource.getApiVersion()); - assertEquals("Pod", deploymentResource.getKind()); - assertEquals("example-pod", deploymentResource.getMetadata().getName()); - } - - @Test - public void testResourceGetFromLoadWhenSingleDocumentsWithStartingDelimiter() throws Exception { - // given - KubernetesClient client = server.getClient(); - - // when - List result = client.load(getClass().getResourceAsStream("/test-template.yml")).get(); - - // then - assertNotNull(result); - assertEquals(5, result.size()); - HasMetadata deploymentResource = result.get(1); - assertEquals("v1", deploymentResource.getApiVersion()); - assertEquals("ImageStream", deploymentResource.getKind()); - assertEquals("eap-app", deploymentResource.getMetadata().getName()); - } - @Test void testNetworkPolicyLoad() { KubernetesClient client = server.getClient(); diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/VersionInfoTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/VersionInfoTest.java index 15ee34f8a7c..fb499551ea0 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/VersionInfoTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/VersionInfoTest.java @@ -35,9 +35,6 @@ public class VersionInfoTest { @Rule public KubernetesServer server = new KubernetesServer(); - @Rule - public OpenShiftServer openshiftServer = new OpenShiftServer(); - @Test public void testClusterVersioning() throws ParseException { server.expect().withPath("/version").andReturn(200, "{" + @@ -56,96 +53,4 @@ public void testClusterVersioning() throws ParseException { assertEquals(client.getVersion().getBuildDate().getYear(), 118); assertEquals(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse("2018-03-01T14:27:17Z").getTime(), client.getVersion().getBuildDate().getTime()); } - - @Test - public void testClusterVersioningOpenshift4() throws ParseException { - openshiftServer.expect().get().withPath("/openshift/version").andReturn(404, "").always(); - - openshiftServer.expect().get().withPath("/apis/config.openshift.io/v1/clusterversions").andReturn(200, "{" + - " \"apiVersion\": \"config.openshift.io/v1\"," + - " \"items\": [" + - " {" + - " \"apiVersion\": \"config.openshift.io/v1\"," + - " \"kind\": \"ClusterVersion\"," + - " \"metadata\": {" + - " \"creationTimestamp\": \"2020-01-30T04:05:33Z\"," + - " \"generation\": 2," + - " \"name\": \"version\"," + - " \"resourceVersion\": \"44758\"," + - " \"selfLink\": \"/apis/config.openshift.io/v1/clusterversions/version\"," + - " \"uid\": \"c301755d-4315-11ea-9872-52540022f9dd\"" + - " }," + - " \"spec\": {" + - " \"channel\": \"stable-4.2\"," + - " \"clusterID\": \"c92835a5-395d-4448-840d-6a223f9a02c7\"," + - " \"upstream\": \"https://api.openshift.com/api/upgrades_info/v1/graph\"" + - " }," + - " \"status\": {" + - " \"availableUpdates\": [" + - " {" + - " \"force\": false," + - " \"image\": \"quay.io/openshift-release-dev/ocp-release@sha256:e5a6e348721c38a78d9299284fbb5c60fb340135a86b674b038500bf190ad514\"," + - " \"version\": \"4.2.16\"" + - " }" + - " ]," + - " \"conditions\": [" + - " {" + - " \"lastTransitionTime\": \"2020-01-30T04:05:35Z\"," + - " \"status\": \"False\"," + - " \"type\": \"Available\"" + - " }," + - " {" + - " \"lastTransitionTime\": \"2020-01-30T04:31:38Z\"," + - " \"message\": \"Cluster operator monitoring is still updating\"," + - " \"reason\": \"ClusterOperatorNotAvailable\"," + - " \"status\": \"True\"," + - " \"type\": \"Failing\"" + - " }," + - " {" + - " \"lastTransitionTime\": \"2020-01-30T04:05:35Z\"," + - " \"message\": \"Unable to apply 4.2.14: the cluster operator monitoring has not yet successfully rolled out\"," + - " \"reason\": \"ClusterOperatorNotAvailable\"," + - " \"status\": \"True\"," + - " \"type\": \"Progressing\"" + - " }," + - " {" + - " \"lastTransitionTime\": \"2020-01-30T04:05:36Z\"," + - " \"status\": \"True\"," + - " \"type\": \"RetrievedUpdates\"" + - " }" + - " ]," + - " \"desired\": {" + - " \"force\": false," + - " \"image\": \"quay.io/openshift-release-dev/ocp-release@sha256:3fabe939da31f9a31f509251b9f73d321e367aba2d09ff392c2f452f6433a95a\"," + - " \"version\": \"4.2.14\"" + - " }," + - " \"history\": [" + - " {" + - " \"completionTime\": null," + - " \"image\": \"quay.io/openshift-release-dev/ocp-release@sha256:3fabe939da31f9a31f509251b9f73d321e367aba2d09ff392c2f452f6433a95a\"," + - " \"startedTime\": \"2020-01-30T04:05:35Z\"," + - " \"state\": \"Partial\"," + - " \"verified\": false," + - " \"version\": \"4.2.14\"" + - " }" + - " ]," + - " \"observedGeneration\": 1," + - " \"versionHash\": \"kEuk9MZ8sK4=\"" + - " }" + - " }" + - " ]," + - " \"kind\": \"ClusterVersionList\"," + - " \"metadata\": {" + - " \"continue\": \"\"," + - " \"resourceVersion\": \"62446\"," + - " \"selfLink\": \"/apis/config.openshift.io/v1/clusterversions\"" + - " }" + - "}").once(); - - OpenShiftClient openShiftClient = openshiftServer.getOpenshiftClient(); - - VersionInfo versionInfo = openShiftClient.getVersion(); - assertEquals("4", versionInfo.getMajor()); - assertEquals("2.14", versionInfo.getMinor()); - } } diff --git a/kubernetes-tests/src/test/java/io/fabric8/openshift/client/server/mock/OpenShiftLoadTest.java b/kubernetes-tests/src/test/java/io/fabric8/openshift/client/server/mock/OpenShiftLoadTest.java new file mode 100644 index 00000000000..b59d9559298 --- /dev/null +++ b/kubernetes-tests/src/test/java/io/fabric8/openshift/client/server/mock/OpenShiftLoadTest.java @@ -0,0 +1,67 @@ +/** + * 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.openshift.client.server.mock; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.openshift.client.OpenShiftClient; +import org.junit.Rule; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@EnableRuleMigrationSupport +class OpenShiftLoadTest { + @Rule + public OpenShiftServer server = new OpenShiftServer(); + + @Test + void testResourceGetFromLoadWhenSingleDocumentsWithStartingDelimiter() { + // given + OpenShiftClient client = server.getOpenshiftClient(); + + // when + List result = client.load(getClass().getResourceAsStream("/test-template.yml")).get(); + + // then + assertNotNull(result); + assertEquals(5, result.size()); + HasMetadata deploymentResource = result.get(1); + assertEquals("v1", deploymentResource.getApiVersion()); + assertEquals("ImageStream", deploymentResource.getKind()); + assertEquals("eap-app", deploymentResource.getMetadata().getName()); + } + + @Test + void testResourceGetFromLoadWhenSingleDocumentsWithoutDelimiter() { + // given + OpenShiftClient client = server.getOpenshiftClient(); + + // when + List result = client.load(getClass().getResourceAsStream("/template-with-params.yml")).get(); + + // then + assertNotNull(result); + assertEquals(1, result.size()); + HasMetadata deploymentResource = result.get(0); + assertEquals("v1", deploymentResource.getApiVersion()); + assertEquals("Pod", deploymentResource.getKind()); + assertEquals("example-pod", deploymentResource.getMetadata().getName()); + } +} diff --git a/kubernetes-tests/src/test/java/io/fabric8/openshift/client/server/mock/OpenShiftVersionInfoTest.java b/kubernetes-tests/src/test/java/io/fabric8/openshift/client/server/mock/OpenShiftVersionInfoTest.java new file mode 100644 index 00000000000..d46150fa304 --- /dev/null +++ b/kubernetes-tests/src/test/java/io/fabric8/openshift/client/server/mock/OpenShiftVersionInfoTest.java @@ -0,0 +1,145 @@ +/** + * 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.openshift.client.server.mock; + +import io.fabric8.kubernetes.client.VersionInfo; +import io.fabric8.openshift.client.OpenShiftClient; +import org.junit.Rule; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport; + +import java.text.ParseException; +import java.text.SimpleDateFormat; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@EnableRuleMigrationSupport +class OpenShiftVersionInfoTest { + @Rule + public OpenShiftServer openshiftServer = new OpenShiftServer(); + + @Test + void testClusterVersioningOpenshift3() throws ParseException { + // Given + openshiftServer.expect().withPath("/version/openshift").andReturn(200, "{\"major\":\"3\",\"minor\":\"11+\"," + + "\"gitVersion\":\"v3.11.154\",\"gitCommit\":\"7a097ad820\",\"gitTreeState\":\"\"," + + "\"buildDate\":\"2019-10-31T21:06:55Z\",\"goVersion\":\"\",\"compiler\":\"\"," + + "\"platform\":\"\"}").always(); + + // When + OpenShiftClient client = openshiftServer.getOpenshiftClient(); + + // Then + assertEquals("v3.11.154", client.getVersion().getGitVersion()); + assertEquals("7a097ad820", client.getVersion().getGitCommit()); + assertEquals("3", client.getVersion().getMajor()); + assertEquals("11+", client.getVersion().getMinor()); + assertEquals(119, client.getVersion().getBuildDate().getYear()); + assertEquals(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse("2019-10-31T21:06:55Z").getTime(), client.getVersion().getBuildDate().getTime()); + } + + @Test + void testClusterVersioningOpenshift4() { + openshiftServer.expect().get().withPath("/openshift/version").andReturn(404, "").always(); + + openshiftServer.expect().get().withPath("/apis/config.openshift.io/v1/clusterversions").andReturn(200, "{" + + " \"apiVersion\": \"config.openshift.io/v1\"," + + " \"items\": [" + + " {" + + " \"apiVersion\": \"config.openshift.io/v1\"," + + " \"kind\": \"ClusterVersion\"," + + " \"metadata\": {" + + " \"creationTimestamp\": \"2020-01-30T04:05:33Z\"," + + " \"generation\": 2," + + " \"name\": \"version\"," + + " \"resourceVersion\": \"44758\"," + + " \"selfLink\": \"/apis/config.openshift.io/v1/clusterversions/version\"," + + " \"uid\": \"c301755d-4315-11ea-9872-52540022f9dd\"" + + " }," + + " \"spec\": {" + + " \"channel\": \"stable-4.2\"," + + " \"clusterID\": \"c92835a5-395d-4448-840d-6a223f9a02c7\"," + + " \"upstream\": \"https://api.openshift.com/api/upgrades_info/v1/graph\"" + + " }," + + " \"status\": {" + + " \"availableUpdates\": [" + + " {" + + " \"force\": false," + + " \"image\": \"quay.io/openshift-release-dev/ocp-release@sha256:e5a6e348721c38a78d9299284fbb5c60fb340135a86b674b038500bf190ad514\"," + + " \"version\": \"4.2.16\"" + + " }" + + " ]," + + " \"conditions\": [" + + " {" + + " \"lastTransitionTime\": \"2020-01-30T04:05:35Z\"," + + " \"status\": \"False\"," + + " \"type\": \"Available\"" + + " }," + + " {" + + " \"lastTransitionTime\": \"2020-01-30T04:31:38Z\"," + + " \"message\": \"Cluster operator monitoring is still updating\"," + + " \"reason\": \"ClusterOperatorNotAvailable\"," + + " \"status\": \"True\"," + + " \"type\": \"Failing\"" + + " }," + + " {" + + " \"lastTransitionTime\": \"2020-01-30T04:05:35Z\"," + + " \"message\": \"Unable to apply 4.2.14: the cluster operator monitoring has not yet successfully rolled out\"," + + " \"reason\": \"ClusterOperatorNotAvailable\"," + + " \"status\": \"True\"," + + " \"type\": \"Progressing\"" + + " }," + + " {" + + " \"lastTransitionTime\": \"2020-01-30T04:05:36Z\"," + + " \"status\": \"True\"," + + " \"type\": \"RetrievedUpdates\"" + + " }" + + " ]," + + " \"desired\": {" + + " \"force\": false," + + " \"image\": \"quay.io/openshift-release-dev/ocp-release@sha256:3fabe939da31f9a31f509251b9f73d321e367aba2d09ff392c2f452f6433a95a\"," + + " \"version\": \"4.2.14\"" + + " }," + + " \"history\": [" + + " {" + + " \"completionTime\": null," + + " \"image\": \"quay.io/openshift-release-dev/ocp-release@sha256:3fabe939da31f9a31f509251b9f73d321e367aba2d09ff392c2f452f6433a95a\"," + + " \"startedTime\": \"2020-01-30T04:05:35Z\"," + + " \"state\": \"Partial\"," + + " \"verified\": false," + + " \"version\": \"4.2.14\"" + + " }" + + " ]," + + " \"observedGeneration\": 1," + + " \"versionHash\": \"kEuk9MZ8sK4=\"" + + " }" + + " }" + + " ]," + + " \"kind\": \"ClusterVersionList\"," + + " \"metadata\": {" + + " \"continue\": \"\"," + + " \"resourceVersion\": \"62446\"," + + " \"selfLink\": \"/apis/config.openshift.io/v1/clusterversions\"" + + " }" + + "}").once(); + + OpenShiftClient openShiftClient = openshiftServer.getOpenshiftClient(); + + VersionInfo versionInfo = openShiftClient.getVersion(); + assertEquals("4", versionInfo.getMajor()); + assertEquals("2.14", versionInfo.getMinor()); + } +} diff --git a/openshift-client/pom.xml b/openshift-client/pom.xml index fd06c57818a..d147c45ea0d 100644 --- a/openshift-client/pom.xml +++ b/openshift-client/pom.xml @@ -56,7 +56,11 @@ io.fabric8 kubernetes-client - + + io.fabric8 + openshift-model + + io.fabric8 openshift-model-operator diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/DefaultOpenShiftClient.java b/openshift-client/src/main/java/io/fabric8/openshift/client/DefaultOpenShiftClient.java index cb80e949178..e4866482528 100644 --- a/openshift-client/src/main/java/io/fabric8/openshift/client/DefaultOpenShiftClient.java +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/DefaultOpenShiftClient.java @@ -33,6 +33,7 @@ import io.fabric8.kubernetes.api.model.DoneableSecret; import io.fabric8.kubernetes.api.model.DoneableService; import io.fabric8.kubernetes.api.model.DoneableServiceAccount; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition; import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinitionList; @@ -150,6 +151,8 @@ import io.fabric8.openshift.client.dsl.internal.SecurityContextConstraintsOperationsImpl; import io.fabric8.openshift.client.dsl.internal.TemplateOperationsImpl; import io.fabric8.openshift.client.dsl.internal.UserOperationsImpl; +import io.fabric8.openshift.client.internal.OpenShiftClusterOperationsImpl; +import io.fabric8.openshift.client.internal.OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl; import io.fabric8.openshift.client.internal.OpenShiftOAuthInterceptor; import okhttp3.Authenticator; import okhttp3.OkHttpClient; @@ -157,6 +160,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; import java.util.Objects; import java.util.Collection; import java.util.HashMap; @@ -283,7 +287,8 @@ public MixedOperation load(InputStream is) { - return delegate.load(is); + return new OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl(httpClient, getConfiguration(), getNamespace(), null, false, false, new ArrayList<>(), is, null, true, DeletionPropagation.BACKGROUND) { + }; } @Override @@ -297,13 +302,14 @@ public NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicable resourceList(KubernetesResourceList is) { - return delegate.resourceList(is); + public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable resourceList(KubernetesResourceList item) { + return new OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl(httpClient, getConfiguration(), getNamespace(), null, false, false, new ArrayList<>(), item, null, DeletionPropagation.BACKGROUND, true) { + }; } @Override public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable resourceList(HasMetadata... items) { - return delegate.resourceList(items); + return resourceList(new KubernetesListBuilder().withItems(items).build()); } @Override @@ -313,7 +319,8 @@ public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable resourceList(String s) { - return delegate.resourceList(s); + return new OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl(httpClient, getConfiguration(), getNamespace(), null, false, false, new ArrayList<>(), s, null, DeletionPropagation.BACKGROUND, true) { + }; } @Override @@ -576,7 +583,7 @@ public ExtensionsAPIGroupClient extensions() { @Override public VersionInfo getVersion() { - return new ClusterOperationsImpl(httpClient, getConfiguration(), ClusterOperationsImpl.OPENSHIFT_VERSION_ENDPOINT).fetchVersion(); + return new OpenShiftClusterOperationsImpl(httpClient, getConfiguration(), OpenShiftClusterOperationsImpl.OPENSHIFT_VERSION_ENDPOINT).fetchVersion(); } @Override diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/BuildOperationsImpl.java b/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/BuildOperationsImpl.java index 64fdaab6606..c0403298b86 100644 --- a/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/BuildOperationsImpl.java +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/BuildOperationsImpl.java @@ -28,8 +28,10 @@ import io.fabric8.kubernetes.client.dsl.base.OperationContext; import io.fabric8.kubernetes.client.dsl.internal.LogWatchCallback; import io.fabric8.kubernetes.client.utils.PodOperationUtil; +import io.fabric8.kubernetes.client.internal.PatchUtils; import io.fabric8.kubernetes.client.utils.URLUtils; import io.fabric8.openshift.client.dsl.BuildResource; +import io.fabric8.openshift.client.internal.patchmixins.BuildMixIn; import okhttp3.OkHttpClient; import io.fabric8.openshift.api.model.Build; import io.fabric8.openshift.api.model.BuildList; @@ -104,6 +106,7 @@ public BuildOperationsImpl(BuildOperationContext context) { this.withPrettyOutput = context.isPrettyOutput(); this.version = context.getVersion(); this.limitBytes = context.getLimitBytes(); + PatchUtils.addMixInToMapper(Build.class, BuildMixIn.class); } private BuildOperationsImpl(BuildOperationContext context, Integer podLogWaitTimeout) { diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/OpenShiftOperation.java b/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/OpenShiftOperation.java index 92a4cee0f2a..45d9573c9cb 100644 --- a/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/OpenShiftOperation.java +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/OpenShiftOperation.java @@ -27,9 +27,12 @@ import io.fabric8.kubernetes.client.utils.Utils; import io.fabric8.openshift.client.OpenShiftConfig; import io.fabric8.openshift.client.OpenShiftConfigBuilder; +import io.fabric8.openshift.client.internal.readiness.OpenShiftReadiness; import java.net.MalformedURLException; import java.net.URL; +import java.util.Objects; +import java.util.concurrent.TimeUnit; public class OpenShiftOperation, D extends Doneable, R extends Resource> extends HasMetadataOperation { @@ -98,7 +101,12 @@ public URL getRootUrl() { } } - protected Class getConfigType() { + @Override + public T waitUntilReady(long amount, TimeUnit timeUnit) throws InterruptedException { + return waitUntilCondition(resource -> Objects.nonNull(resource) && OpenShiftReadiness.isReady(resource), amount, timeUnit); + } + + protected Class getConfigType() { return OpenShiftConfig.class; } } diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/internal/OpenShiftClusterOperationsImpl.java b/openshift-client/src/main/java/io/fabric8/openshift/client/internal/OpenShiftClusterOperationsImpl.java new file mode 100644 index 00000000000..6cf2eec5b9f --- /dev/null +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/internal/OpenShiftClusterOperationsImpl.java @@ -0,0 +1,76 @@ +/** + * 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.openshift.client.internal; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.VersionInfo; +import io.fabric8.kubernetes.client.dsl.internal.ClusterOperationsImpl; +import io.fabric8.kubernetes.client.utils.Serialization; +import io.fabric8.openshift.api.model.ClusterVersion; +import io.fabric8.openshift.api.model.ClusterVersionList; +import okhttp3.OkHttpClient; +import okhttp3.Response; + +import java.io.IOException; +import java.text.ParseException; + +public class OpenShiftClusterOperationsImpl extends ClusterOperationsImpl { + public static final String OPENSHIFT_VERSION_ENDPOINT = "version/openshift"; + public static final String OPENSHIFT4_VERSION_ENDPOINT = "apis/config.openshift.io/v1/clusterversions"; + + public OpenShiftClusterOperationsImpl(OkHttpClient client, Config config, String item) { + super(client, config, item); + } + + @Override + public VersionInfo fetchVersion() { + try { + // Try to get version using OpenShift 3 URLs. + // Method will throw Exception if it failed + return super.fetchVersion(); + } catch (KubernetesClientException | NullPointerException exception) { + try { + // Handle Openshift 4 version case + return fetchOpenshift4Version(); + } catch (Exception e) { + throw KubernetesClientException.launderThrowable(e); + } + } + } + + private VersionInfo fetchOpenshift4Version() throws IOException, ParseException { + Response response = handleVersionGet(OPENSHIFT4_VERSION_ENDPOINT); + if (response.isSuccessful() && response.body() != null) { + ClusterVersionList clusterVersionList = Serialization.jsonMapper().readValue(response.body().string(), ClusterVersionList.class); + if (!clusterVersionList.getItems().isEmpty()) { + return parseVersionInfoFromClusterVersion(clusterVersionList.getItems().get(0)); + } + } + return null; + } + + private static VersionInfo parseVersionInfoFromClusterVersion(ClusterVersion clusterVersion) throws ParseException { + String[] versionParts = clusterVersion.getStatus().getDesired().getVersion().split("\\."); + VersionInfo.Builder versionInfoBuilder = new VersionInfo.Builder(); + if (versionParts.length == 3) { + versionInfoBuilder.withMajor(versionParts[0]); + versionInfoBuilder.withMinor(versionParts[1] + "." + versionParts[2]); + } + versionInfoBuilder.withBuildDate(clusterVersion.getMetadata().getCreationTimestamp()); + return versionInfoBuilder.build(); + } +} diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/internal/OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableImpl.java b/openshift-client/src/main/java/io/fabric8/openshift/client/internal/OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableImpl.java new file mode 100644 index 00000000000..e0526b8b0b3 --- /dev/null +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/internal/OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableImpl.java @@ -0,0 +1,41 @@ +/** + * 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.openshift.client.internal; + +import io.fabric8.kubernetes.api.builder.Visitor; +import io.fabric8.kubernetes.api.model.DeletionPropagation; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.dsl.internal.NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableImpl; +import io.fabric8.openshift.client.internal.readiness.OpenShiftReadiness; +import okhttp3.OkHttpClient; + +import java.io.InputStream; +import java.util.List; + +public class OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableImpl extends NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableImpl { + public OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableImpl(OkHttpClient client, Config config, String namespace, String explicitNamespace, Boolean fromServer, Boolean deletingExisting, List visitors, InputStream is, Boolean cascading, long watchRetryInitialBackoffMillis, double watchRetryBackoffMultiplier) { + super(client, config, namespace, explicitNamespace, fromServer, deletingExisting, visitors, is, cascading, watchRetryInitialBackoffMillis, watchRetryBackoffMultiplier); + } + + public OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableImpl(OkHttpClient client, Config config, String namespace, String explicitNamespace, Boolean fromServer, Boolean deletingExisting, List visitors, Object item, long gracePeriodSeconds, DeletionPropagation propagationPolicy, Boolean cascading, long watchRetryInitialBackoffMillis, double watchRetryBackoffMultiplier) { + super(client, config, namespace, explicitNamespace, fromServer, deletingExisting, visitors, item, gracePeriodSeconds, propagationPolicy, cascading, watchRetryInitialBackoffMillis, watchRetryBackoffMultiplier); + } + + @Override + public Boolean isReady() { + return OpenShiftReadiness.isReady(get()); + } +} diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/internal/OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java b/openshift-client/src/main/java/io/fabric8/openshift/client/internal/OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java new file mode 100644 index 00000000000..28fe3e79d7f --- /dev/null +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/internal/OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java @@ -0,0 +1,135 @@ +/** + * 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.openshift.client.internal; + +import com.mifmif.common.regex.Generex; +import io.fabric8.kubernetes.api.builder.Visitor; +import io.fabric8.kubernetes.api.model.DeletionPropagation; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesList; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.kubernetes.api.model.KubernetesResourceList; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.dsl.internal.NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl; +import io.fabric8.kubernetes.client.utils.Utils; +import io.fabric8.openshift.api.model.Parameter; +import io.fabric8.openshift.api.model.Template; +import io.fabric8.openshift.client.internal.readiness.OpenShiftReadiness; +import okhttp3.OkHttpClient; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl extends NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl { + public OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl(OkHttpClient client, Config config, String namespace, String explicitNamespace, Boolean fromServer, Boolean deletingExisting, List visitors, InputStream is, Map parameters, Boolean cascading, DeletionPropagation propagationPolicy) { + super(client, config, namespace, explicitNamespace, fromServer, deletingExisting, visitors, is, parameters, cascading, propagationPolicy); + } + + public OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl(OkHttpClient client, Config config, String namespace, String explicitNamespace, Boolean fromServer, Boolean deletingExisting, List visitors, Object item, Map parameters, DeletionPropagation propagationPolicy, Boolean cascading) { + super(client, config, namespace, explicitNamespace, fromServer, deletingExisting, visitors, item, parameters, propagationPolicy, cascading); + } + + public OpenShiftNamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl(OkHttpClient client, Config config, String namespace, String explicitNamespace, Boolean fromServer, Boolean deletingExisting, List visitors, Object item, InputStream inputStream, Map parameters, long gracePeriodSeconds, DeletionPropagation propagationPolicy, Boolean cascading, long watchRetryInitialBackoffMillis, double watchRetryBackoffMultiplier) { + super(client, config, namespace, explicitNamespace, fromServer, deletingExisting, visitors, item, inputStream, parameters, gracePeriodSeconds, propagationPolicy, cascading, watchRetryInitialBackoffMillis, watchRetryBackoffMultiplier); + } + + protected boolean isResourceReady(HasMetadata meta) { + return OpenShiftReadiness.isReady(meta); + } + + @Override + protected List asHasMetadata(T item, Boolean enableProccessing) { + List result = new ArrayList<>(); + if (item instanceof KubernetesList) { + result.addAll(((KubernetesList) item).getItems()); + } else if (item instanceof Template) { + result.addAll(processTemplateList((Template)item, enableProccessing)); + } else if (item instanceof KubernetesResourceList) { + result.addAll(((KubernetesResourceList) item).getItems()); + } else if (item instanceof HasMetadata) { + result.add((HasMetadata) item); + } else if (item instanceof String) { + try (InputStream is = new ByteArrayInputStream(((String) item).getBytes(StandardCharsets.UTF_8))) { + return asHasMetadata(unmarshal(is), enableProccessing); + } catch (IOException e) { + throw KubernetesClientException.launderThrowable(e); + } + } else if (item instanceof Collection) { + for (Object o : (Collection) item) { + if (o instanceof HasMetadata) { + result.add((HasMetadata) o); + } + } + } + return result; + } + + private static List processTemplate(Template template, Boolean failOnMissing) { + List parameters = template != null ? template.getParameters() : null; + List objects = template != null ? template.getObjects() : Collections.emptyList(); + KubernetesList list = new KubernetesListBuilder() + .withItems(objects) + .build(); + + try { + String json = OBJECT_MAPPER.writeValueAsString(list); + if (parameters != null && !parameters.isEmpty()) { + // lets make a few passes in case there's expressions in values + for (int i = 0; i < 5; i++) { + for (Parameter parameter : parameters) { + String name = parameter.getName(); + String regex = "${" + name + "}"; + String value; + if (Utils.isNotNullOrEmpty(parameter.getValue())) { + value = parameter.getValue(); + } else if (EXPRESSION.equals(parameter.getGenerate())) { + Generex generex = new Generex(parameter.getFrom()); + value = generex.random(); + } else if (Boolean.TRUE.equals(failOnMissing)) { + throw new IllegalArgumentException("No value available for parameter name: " + name); + } else { + value = ""; + } + json = json.replace(regex, value); + } + } + } + + list = OBJECT_MAPPER.readValue(json, KubernetesList.class); + } catch (IOException e) { + throw KubernetesClientException.launderThrowable(e); + } + return list.getItems(); + } + + private static List processTemplateList(Template item, Boolean enableProccessing) { + List result = new ArrayList<>(); + if (Boolean.FALSE.equals(enableProccessing)) { + result.addAll(item.getObjects()); + } else { + result.addAll(processTemplate(item, false)); + } + return result; + } +} diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/patchmixins/BuildMixIn.java b/openshift-client/src/main/java/io/fabric8/openshift/client/internal/patchmixins/BuildMixIn.java similarity index 93% rename from kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/patchmixins/BuildMixIn.java rename to openshift-client/src/main/java/io/fabric8/openshift/client/internal/patchmixins/BuildMixIn.java index d75687eab8f..04124985c58 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/patchmixins/BuildMixIn.java +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/internal/patchmixins/BuildMixIn.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.fabric8.kubernetes.client.internal.patchmixins; +package io.fabric8.openshift.client.internal.patchmixins; import com.fasterxml.jackson.annotation.JsonIgnore; import io.fabric8.openshift.api.model.Build; diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/internal/readiness/OpenShiftReadiness.java b/openshift-client/src/main/java/io/fabric8/openshift/client/internal/readiness/OpenShiftReadiness.java new file mode 100644 index 00000000000..97e9c7fe53b --- /dev/null +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/internal/readiness/OpenShiftReadiness.java @@ -0,0 +1,53 @@ +/** + * 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.openshift.client.internal.readiness; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.internal.readiness.Readiness; +import io.fabric8.kubernetes.client.utils.Utils; +import io.fabric8.openshift.api.model.DeploymentConfig; +import io.fabric8.openshift.api.model.DeploymentConfigSpec; +import io.fabric8.openshift.api.model.DeploymentConfigStatus; + +public class OpenShiftReadiness extends Readiness { + public static boolean isReady(HasMetadata item) { + if (Readiness.isReadiableKubernetesResource(item)) { + return Readiness.isReady(item); + } else if (item instanceof DeploymentConfig) { + return isDeploymentConfigReady((DeploymentConfig) item); + } else { + throw new IllegalArgumentException("Item needs to be one of [Node, Deployment, ReplicaSet, StatefulSet, Pod, DeploymentConfig, ReplicationController], but was: [" + (item != null ? item.getKind() : "Unknown (null)") + "]"); + } + } + + public static boolean isDeploymentConfigReady(DeploymentConfig d) { + Utils.checkNotNull(d, "Deployment can't be null."); + DeploymentConfigSpec spec = d.getSpec(); + DeploymentConfigStatus status = d.getStatus(); + + if (status == null || status.getReplicas() == null || status.getAvailableReplicas() == null) { + return false; + } + + //Can be true in testing, so handle it to make test writing easier. + if (spec == null || spec.getReplicas() == null) { + return false; + } + + return spec.getReplicas().intValue() == status.getReplicas() && + spec.getReplicas() <= status.getAvailableReplicas(); + } +} diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/base/TestUnmarshal.java b/openshift-client/src/test/java/io/fabric8/openshift/client/dsl/base/TestUnmarshal.java similarity index 65% rename from kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/base/TestUnmarshal.java rename to openshift-client/src/test/java/io/fabric8/openshift/client/dsl/base/TestUnmarshal.java index a82c0b6275b..c3637294efa 100644 --- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/base/TestUnmarshal.java +++ b/openshift-client/src/test/java/io/fabric8/openshift/client/dsl/base/TestUnmarshal.java @@ -13,26 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.fabric8.kubernetes.client.dsl.base; +package io.fabric8.openshift.client.dsl.base; +import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.openshift.api.model.Template; import org.junit.jupiter.api.Test; -import java.io.IOException; - import static org.junit.jupiter.api.Assertions.assertEquals; -public class TestUnmarshal { +class TestUnmarshal { @Test - public void testUnmarshalJSONTemplate() throws IOException { - Template t = new OperationSupport().unmarshal(getClass().getResourceAsStream("/test-template.json"), Template.class); + void testUnmarshalJSONTemplate() { + Template t = Serialization.unmarshal(getClass().getResourceAsStream("/test-template.json"), Template.class); assertEquals("eap6-basic-sti", t.getMetadata().getName()); } @Test - public void testUnmarshalYAMLTemplate() throws IOException { - Template t = new OperationSupport().unmarshal(getClass().getResourceAsStream("/test-template.yml"), Template.class); + void testUnmarshalYAMLTemplate() { + Template t = Serialization.unmarshal(getClass().getResourceAsStream("/test-template.yml"), Template.class); assertEquals("eap6-basic-sti", t.getMetadata().getName()); }