diff --git a/CHANGELOG.md b/CHANGELOG.md index fcadfb02cb..6f2972b671 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Fix #4574: fromServer has been deprecated - it no longer needs to be called. All get() operations will fetch the resource(s) from the api server. If you need the context item that was passed in from a resource, load, or resourceList methods, use the item or items method. * Fix #4633: client.run().withRunConfig was deprecated. Use withNewRunConfig instead. * Fix #4663: Config.maxConcurrentRequests and Config.maxConcurrentRequestsPerHost will no longer be used. Instead they will default to unlimited for all clients. Due to the ability of the fabric8 client to start long running requests (either websocket or regular http) and how this is treated by the underlying clients you can easily exhaust these values and enter a state where the client is unresponsive without any additional information on what is occurring. +* Fix #4014: Support for Openshift 3.9 and 3.10 have been dropped because the RoleBinding logic no longer supports setting both the subjects and groups and the usernames and groupnames. You may still use a newer client against legacy versions if you take responsibility for setting the usernames and groupnames fields on your own. ### 6.3.1 (2022-12-15) diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Client.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Client.java index 9ef0d45e74..e4a171808e 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Client.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Client.java @@ -24,6 +24,7 @@ import io.fabric8.kubernetes.api.model.KubernetesResourceList; import io.fabric8.kubernetes.api.model.RootPaths; import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.NamespacedInOutCreateable; import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.kubernetes.client.http.HttpClient; @@ -142,6 +143,19 @@ public interface Client extends Closeable { , R extends Resource> MixedOperation resources( Class resourceType, Class listClass, Class resourceClass); + /** + * Typed API for managing create only resources. + * + * @param + * @param + * @param inType + * @param outType + * @return + */ + NamespacedInOutCreateable createOnlyResources( + Class inType, + Class outType); + /** * Typed API for managing resources. Any properly annotated POJO can be utilized as a resource. * diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ServiceToURLProvider.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ServiceToURLProvider.java index 98d96a5620..0f3422aea7 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ServiceToURLProvider.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ServiceToURLProvider.java @@ -17,6 +17,9 @@ package io.fabric8.kubernetes.client; import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.ServicePort; + +import java.util.Objects; public interface ServiceToURLProvider { enum ServiceToUrlImplPriority { @@ -40,4 +43,17 @@ public int getValue() { int getPriority(); String getURL(Service service, String portName, String namespace, KubernetesClient client); + + static ServicePort getServicePortByName(Service service, String portName) { + if (portName.isEmpty()) { + return service.getSpec().getPorts().iterator().next(); + } + + for (ServicePort servicePort : service.getSpec().getPorts()) { + if (Objects.equals(servicePort.getName(), portName)) { + return servicePort; + } + } + return null; + } } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/dsl/NamespacedInOutCreateable.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/dsl/NamespacedInOutCreateable.java index e38a4646d0..06a8efe7cc 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/dsl/NamespacedInOutCreateable.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/dsl/NamespacedInOutCreateable.java @@ -16,5 +16,5 @@ package io.fabric8.kubernetes.client.dsl; -public interface NamespacedInOutCreateable extends Namespaceable> { +public interface NamespacedInOutCreateable extends Namespaceable>, InOutCreateable { } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ClientAdapter.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ClientAdapter.java index a8f9028b40..edf6c656aa 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ClientAdapter.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ClientAdapter.java @@ -28,6 +28,7 @@ import io.fabric8.kubernetes.client.NamespacedKubernetesClient; import io.fabric8.kubernetes.client.RequestConfig; import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.NamespacedInOutCreateable; import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.kubernetes.client.http.HttpClient; @@ -129,6 +130,12 @@ public , R extends Re return client.resources(resourceType, listClass, resourceClass); } + @Override + public NamespacedInOutCreateable createOnlyResources( + Class inType, Class outType) { + return client.createOnlyResources(inType, outType); + } + public C inAnyNamespace() { C result = newInstance(); result.init(client.adapt(NamespacedKubernetesClient.class).inAnyNamespace()); diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleMixedOperation.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleMixedOperation.java new file mode 100644 index 0000000000..e2b82fc243 --- /dev/null +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleMixedOperation.java @@ -0,0 +1,349 @@ +/** + * 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.extension; + +import io.fabric8.kubernetes.api.model.DeletionPropagation; +import io.fabric8.kubernetes.api.model.LabelSelector; +import io.fabric8.kubernetes.api.model.ListOptions; +import io.fabric8.kubernetes.api.model.ObjectReference; +import io.fabric8.kubernetes.api.model.StatusDetails; +import io.fabric8.kubernetes.client.Client; +import io.fabric8.kubernetes.client.GracePeriodConfigurable; +import io.fabric8.kubernetes.client.PropagationPolicyConfigurable; +import io.fabric8.kubernetes.client.Watch; +import io.fabric8.kubernetes.client.Watcher; +import io.fabric8.kubernetes.client.dsl.AnyNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.Deletable; +import io.fabric8.kubernetes.client.dsl.DeletableWithOptions; +import io.fabric8.kubernetes.client.dsl.FilterNested; +import io.fabric8.kubernetes.client.dsl.FilterWatchListDeletable; +import io.fabric8.kubernetes.client.dsl.Informable; +import io.fabric8.kubernetes.client.dsl.ItemWritableOperation; +import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.dsl.Watchable; +import io.fabric8.kubernetes.client.informers.ResourceEventHandler; +import io.fabric8.kubernetes.client.informers.SharedIndexInformer; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class ExtensibleMixedOperation> implements MixedOperation { + + protected final MixedOperation operation; + protected final Client client; + + public ExtensibleMixedOperation(Client client, MixedOperation operation) { + this.client = client; + this.operation = operation; + } + + @Override + public R withName(String name) { + return operation.withName(name); + } + + @Override + public NonNamespaceOperation inNamespace(String name) { + return operation.inNamespace(name); + } + + @Override + public T replaceStatus(T item) { + return operation.replaceStatus(item); + } + + @Override + public AnyNamespaceOperation inAnyNamespace() { + return operation.inAnyNamespace(); + } + + @Override + public Watchable withResourceVersion(String resourceVersion) { + return operation.withResourceVersion(resourceVersion); + } + + @Override + public L list() { + return operation.list(); + } + + @Override + public L list(Integer limitVal, String continueVal) { + return operation.list(limitVal, continueVal); + } + + @Override + public List delete() { + return operation.delete(); + } + + @Override + public T waitUntilReady(long amount, TimeUnit timeUnit) { + return operation.waitUntilReady(amount, timeUnit); + } + + @Override + public PropagationPolicyConfigurable withGracePeriod(long gracePeriodSeconds) { + return operation.withGracePeriod(gracePeriodSeconds); + } + + @Override + public T waitUntilCondition(Predicate condition, long amount, TimeUnit timeUnit) { + return operation.waitUntilCondition(condition, amount, timeUnit); + } + + @Override + public T createOrReplace(T item) { + return operation.createOrReplace(item); + } + + @Override + public Watch watch(Watcher watcher) { + return operation.watch(watcher); + } + + @Override + public GracePeriodConfigurable withPropagationPolicy(DeletionPropagation propagationPolicy) { + return operation.withPropagationPolicy(propagationPolicy); + } + + @Override + public FilterNested> withNewFilter() { + return operation.withNewFilter(); + } + + @Override + public T replace(T item) { + return operation.replace(item); + } + + @Override + public FilterWatchListDeletable withLabels(Map labels) { + return operation.withLabels(labels); + } + + @Override + public ItemWritableOperation dryRun() { + return operation.dryRun(); + } + + @Override + public FilterWatchListDeletable withoutLabels(Map labels) { + return operation.withoutLabels(labels); + } + + @Override + public DeletableWithOptions withTimeout(long timeout, TimeUnit unit) { + return operation.withTimeout(timeout, unit); + } + + @Override + public Watch watch(ListOptions options, Watcher watcher) { + return operation.watch(options, watcher); + } + + @Override + public L list(ListOptions listOptions) { + return operation.list(listOptions); + } + + @Override + public Informable withIndexers(Map>> indexers) { + return operation.withIndexers(indexers); + } + + @Override + public Stream resources() { + return operation.resources(); + } + + @Override + public DeletableWithOptions withTimeoutInMillis(long timeoutInMillis) { + return operation.withTimeoutInMillis(timeoutInMillis); + } + + @Override + public T create(T item) { + return operation.create(item); + } + + @Override + public FilterWatchListDeletable withLabelIn(String key, String... values) { + return operation.withLabelIn(key, values); + } + + @Override + public List delete(T item) { + return operation.delete(item); + } + + @Override + public FilterWatchListDeletable withLabelNotIn(String key, String... values) { + return operation.withLabelNotIn(key, values); + } + + @Override + public FilterWatchListDeletable withLabel(String key, String value) { + return operation.withLabel(key, value); + } + + @Override + public Informable withLimit(Long limit) { + return operation.withLimit(limit); + } + + @Override + public ItemWritableOperation dryRun(boolean isDryRun) { + return operation.dryRun(isDryRun); + } + + @Override + public FilterWatchListDeletable withLabel(String key) { + return operation.withLabel(key); + } + + @Override + public T updateStatus(T item) { + return operation.updateStatus(item); + } + + @Override + public FilterWatchListDeletable withoutLabel(String key, String value) { + return operation.withoutLabel(key, value); + } + + @Override + public Watch watch(String resourceVersion, Watcher watcher) { + return operation.watch(resourceVersion, watcher); + } + + @Override + public FilterWatchListDeletable withoutLabel(String key) { + return operation.withoutLabel(key); + } + + @Override + public FilterWatchListDeletable withFields(Map fields) { + return operation.withFields(fields); + } + + @Override + public FilterWatchListDeletable withField(String key, String value) { + return operation.withField(key, value); + } + + @Override + public FilterWatchListDeletable withoutFields(Map fields) { + return operation.withoutFields(fields); + } + + @Override + public T patchStatus(T item) { + return operation.patchStatus(item); + } + + @Override + public boolean delete(T... items) { + return operation.delete(items); + } + + @Override + public SharedIndexInformer inform() { + return operation.inform(); + } + + @Override + public FilterWatchListDeletable withoutField(String key, String value) { + return operation.withoutField(key, value); + } + + @Override + public FilterWatchListDeletable withLabelSelector(LabelSelector selector) { + return operation.withLabelSelector(selector); + } + + @Override + public boolean delete(List items) { + return operation.delete(items); + } + + @Override + public FilterWatchListDeletable withLabelSelector(String selectorAsString) { + return operation.withLabelSelector(selectorAsString); + } + + @Override + public FilterWatchListDeletable withInvolvedObject(ObjectReference objectReference) { + return operation.withInvolvedObject(objectReference); + } + + @Override + public R load(InputStream is) { + return operation.load(is); + } + + @Override + public R load(URL url) { + return operation.load(url); + } + + @Override + public SharedIndexInformer inform(ResourceEventHandler handler) { + return operation.inform(handler); + } + + @Override + public R load(File file) { + return operation.load(file); + } + + @Override + public R load(String path) { + return operation.load(path); + } + + @Override + public R resource(T item) { + return operation.resource(item); + } + + @Override + public SharedIndexInformer inform(ResourceEventHandler handler, long resync) { + return operation.inform(handler, resync); + } + + @Override + public SharedIndexInformer runnableInformer(long resync) { + return operation.runnableInformer(resync); + } + + @Override + public CompletableFuture> informOnCondition(Predicate> condition) { + return operation.informOnCondition(condition); + } + +} \ No newline at end of file diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleResource.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleResource.java index 443a6f17d0..399bf477c9 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleResource.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleResource.java @@ -18,6 +18,7 @@ import io.fabric8.kubernetes.api.model.DeletionPropagation; import io.fabric8.kubernetes.client.Client; +import io.fabric8.kubernetes.client.OperationInfo; import io.fabric8.kubernetes.client.dsl.Nameable; import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.kubernetes.client.dsl.WritableOperation; @@ -94,4 +95,14 @@ default WritableOperation withTimeoutInMillis(long timeoutInMillis) { return withTimeout(timeoutInMillis, TimeUnit.MILLISECONDS); } + OperationInfo forOperationType(String type); + + enum Scope { + NAMESPACE, + TYPE, + RESOURCE + } + + X operation(Scope scope, String method, Object payload, Class responseType, String... path); + } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleResourceAdapter.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleResourceAdapter.java index 26d5a1a9f7..50591a6594 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleResourceAdapter.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extension/ExtensibleResourceAdapter.java @@ -18,6 +18,7 @@ import io.fabric8.kubernetes.api.model.DeletionPropagation; import io.fabric8.kubernetes.client.Client; +import io.fabric8.kubernetes.client.OperationInfo; import java.util.List; import java.util.Map; @@ -127,4 +128,14 @@ public ExtensibleResource withTimeoutInMillis(long timeoutInMillis) { return withTimeout(timeoutInMillis, TimeUnit.MILLISECONDS); } + @Override + public OperationInfo forOperationType(String type) { + return this.resource.forOperationType(type); + } + + @Override + public X operation(Scope scope, String method, Object payload, Class responseType, String... path) { + return this.resource.operation(scope, method, payload, responseType, path); + } + } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/BaseOperation.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/BaseOperation.java index 1fd26e7514..75eca20ca4 100755 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/BaseOperation.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/BaseOperation.java @@ -722,27 +722,47 @@ protected T handlePatch(PatchContext context, T current, T updated, boolean stat } protected Scale handleScale(Scale scaleParam) { - try { - return handleScale(getCompleteResourceUrl().toString(), scaleParam); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - throw KubernetesClientException.launderThrowable(forOperationType("scale"), ie); - } catch (IOException e) { - throw KubernetesClientException.launderThrowable(forOperationType("scale"), e); - } - + return operation(Scope.RESOURCE, "PUT", scaleParam, Scale.class, "scale"); } protected Status handleDeploymentRollback(DeploymentRollback deploymentRollback) { + return operation(Scope.RESOURCE, "POST", deploymentRollback, Status.class, "rollback"); + } + + protected T handleApproveOrDeny(T csr) { + return operation(Scope.RESOURCE, "PUT", csr, type, "approval"); + } + + @Override + public X operation(Scope scope, String method, Object payload, Class responseType, String... path) { + String operationName = scope.name(); try { - return handleDeploymentRollback(getCompleteResourceUrl().toString(), deploymentRollback); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - throw KubernetesClientException.launderThrowable(forOperationType("rollback"), ie); + URL baseUrl = null; + switch (scope) { + case NAMESPACE: + baseUrl = getNamespacedUrl(namespace, null); + break; + case TYPE: + baseUrl = getNamespacedUrl(); + break; + case RESOURCE: + baseUrl = getCompleteResourceUrl(); + break; + } + String uri = baseUrl.toString(); + if (path != null && path.length > 0) { + operationName = path[0]; + uri = Stream.concat(Stream.of(uri), Stream.of(path)).collect(Collectors.joining("/")); + } + HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder() + .uri(uri); + if (payload != null) { + requestBuilder.method(method, JSON, Serialization.asJson(payload)); + } + return handleResponse(requestBuilder, responseType); } catch (IOException e) { - throw KubernetesClientException.launderThrowable(forOperationType("rollback"), e); + throw KubernetesClientException.launderThrowable(forOperationType(operationName), e); } - } protected T handleGet(URL resourceUrl) throws InterruptedException, IOException { diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupport.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupport.java index 6dad8f97f0..6f310a328c 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupport.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupport.java @@ -25,8 +25,6 @@ import io.fabric8.kubernetes.api.model.Preconditions; import io.fabric8.kubernetes.api.model.Status; import io.fabric8.kubernetes.api.model.StatusBuilder; -import io.fabric8.kubernetes.api.model.autoscaling.v1.Scale; -import io.fabric8.kubernetes.api.model.extensions.DeploymentRollback; import io.fabric8.kubernetes.client.Client; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClientException; @@ -435,39 +433,6 @@ protected T handlePatch(PatchContext patchContext, T current, String patchFo return handleResponse(requestBuilder, type); } - /** - * Replace Scale of specified Kubernetes Resource - * - * @param resourceUrl Kubernetes resource URL - * @param scale Scale object which we want to inject - * @return updated Scale object - * @throws InterruptedException in case thread is interrupted - * @throws IOException in some other I/O problem - */ - protected Scale handleScale(String resourceUrl, Scale scale) throws InterruptedException, IOException { - HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().uri(resourceUrl + "/scale"); - if (scale != null) { - requestBuilder.put(JSON, JSON_MAPPER.writeValueAsString(scale)); - } - return handleResponse(requestBuilder, Scale.class); - } - - /** - * Create rollback of a Deployment - * - * @param resourceUrl resource url - * @param deploymentRollback DeploymentRollback resource - * @return Status - * @throws InterruptedException in case thread is interrupted - * @throws IOException in some other I/O problem - */ - protected Status handleDeploymentRollback(String resourceUrl, DeploymentRollback deploymentRollback) - throws InterruptedException, IOException { - HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().uri(resourceUrl + "/rollback").post(JSON, - JSON_MAPPER.writeValueAsString(deploymentRollback)); - return handleResponse(requestBuilder, Status.class); - } - /** * Send an http get. * @@ -488,13 +453,6 @@ protected Map getParameters() { return Collections.emptyMap(); } - protected T handleApproveOrDeny(T csr, Class type) throws IOException, InterruptedException { - String uri = URLUtils.join(getResourceUrl(null, csr.getMetadata().getName(), false).toString(), "approval"); - HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder() - .put(JSON, JSON_MAPPER.writeValueAsString(csr)).uri(uri); - return handleResponse(requestBuilder, type); - } - /** * Send a raw get - where the type should be one of String, Reader, InputStream *
@@ -559,7 +517,7 @@ protected T waitForResult(CompletableFuture future) throws IOException { * @throws InterruptedException Interrupted Exception * @throws IOException IOException */ - protected T handleResponse(HttpRequest.Builder requestBuilder, Class type) throws InterruptedException, IOException { + protected T handleResponse(HttpRequest.Builder requestBuilder, Class type) throws IOException { return handleResponse(requestBuilder, type, getParameters()); } @@ -810,9 +768,6 @@ public R1 restCall(Class result, String... path) { throw e; } return null; - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - throw KubernetesClientException.launderThrowable(ie); } catch (IOException e) { throw KubernetesClientException.launderThrowable(e); } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/certificates/v1/CertificateSigningRequestOperationsImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/certificates/v1/CertificateSigningRequestOperationsImpl.java index d9a233a6cc..25a29e6770 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/certificates/v1/CertificateSigningRequestOperationsImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/certificates/v1/CertificateSigningRequestOperationsImpl.java @@ -22,14 +22,11 @@ import io.fabric8.kubernetes.api.model.certificates.v1.CertificateSigningRequestStatus; import io.fabric8.kubernetes.api.model.certificates.v1.CertificateSigningRequestStatusBuilder; import io.fabric8.kubernetes.client.Client; -import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.dsl.CertificateSigningRequestResource; import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperation; import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperationsImpl; import io.fabric8.kubernetes.client.dsl.internal.OperationContext; -import java.io.IOException; - public class CertificateSigningRequestOperationsImpl extends HasMetadataOperation> implements CertificateSigningRequestResource { @@ -60,16 +57,9 @@ public CertificateSigningRequest deny(CertificateSigningRequestCondition certifi private CertificateSigningRequest addStatusToCSRAndSubmit( CertificateSigningRequestCondition certificateSigningRequestCondition) { - try { - CertificateSigningRequest fromServerCsr = get(); - fromServerCsr.setStatus(createCertificateSigningRequestStatus(certificateSigningRequestCondition)); - return handleApproveOrDeny(fromServerCsr, CertificateSigningRequest.class); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - throw KubernetesClientException.launderThrowable(forOperationType("approval " + type), ie); - } catch (IOException e) { - throw KubernetesClientException.launderThrowable(forOperationType("approval " + type), e); - } + CertificateSigningRequest fromServerCsr = get(); + fromServerCsr.setStatus(createCertificateSigningRequestStatus(certificateSigningRequestCondition)); + return handleApproveOrDeny(fromServerCsr); } private CertificateSigningRequestStatus createCertificateSigningRequestStatus( diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/certificates/v1beta1/CertificateSigningRequestOperationsImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/certificates/v1beta1/CertificateSigningRequestOperationsImpl.java index 4836b65401..9a74006aa5 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/certificates/v1beta1/CertificateSigningRequestOperationsImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/certificates/v1beta1/CertificateSigningRequestOperationsImpl.java @@ -22,14 +22,11 @@ import io.fabric8.kubernetes.api.model.certificates.v1beta1.CertificateSigningRequestStatus; import io.fabric8.kubernetes.api.model.certificates.v1beta1.CertificateSigningRequestStatusBuilder; import io.fabric8.kubernetes.client.Client; -import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.dsl.V1beta1CertificateSigningRequestResource; import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperation; import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperationsImpl; import io.fabric8.kubernetes.client.dsl.internal.OperationContext; -import java.io.IOException; - public class CertificateSigningRequestOperationsImpl extends HasMetadataOperation> implements V1beta1CertificateSigningRequestResource { @@ -67,15 +64,8 @@ private CertificateSigningRequestStatus createCertificateSigningRequestStatus( private CertificateSigningRequest addStatusToCSRAndSubmit( CertificateSigningRequestCondition certificateSigningRequestCondition) { - try { - CertificateSigningRequest fromServerCsr = get(); - fromServerCsr.setStatus(createCertificateSigningRequestStatus(certificateSigningRequestCondition)); - return handleApproveOrDeny(fromServerCsr, CertificateSigningRequest.class); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - throw KubernetesClientException.launderThrowable(forOperationType("approval " + type), ie); - } catch (IOException e) { - throw KubernetesClientException.launderThrowable(forOperationType("approval " + type), e); - } + CertificateSigningRequest fromServerCsr = get(); + fromServerCsr.setStatus(createCertificateSigningRequestStatus(certificateSigningRequestCondition)); + return handleApproveOrDeny(fromServerCsr); } } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/core/v1/PodOperationsImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/core/v1/PodOperationsImpl.java index 0f08a8ef65..1447e7908a 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/core/v1/PodOperationsImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/core/v1/PodOperationsImpl.java @@ -52,7 +52,6 @@ import io.fabric8.kubernetes.client.dsl.internal.PortForwarderWebsocket; import io.fabric8.kubernetes.client.dsl.internal.uploadable.PodUpload; import io.fabric8.kubernetes.client.http.HttpClient; -import io.fabric8.kubernetes.client.http.HttpRequest; import io.fabric8.kubernetes.client.http.WebSocket; import io.fabric8.kubernetes.client.lib.FilenameUtils; import io.fabric8.kubernetes.client.utils.URLUtils; @@ -251,21 +250,13 @@ private boolean handleEvict(HasMetadata eviction) { throw new KubernetesClientException("Name not specified, but operation requires it."); } - URL requestUrl = new URL(URLUtils.join(getResourceUrl().toString(), "eviction")); - HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder() - .post(JSON, JSON_MAPPER.writeValueAsString(eviction)).url(requestUrl); - handleResponse(requestBuilder, null); + operation(Scope.RESOURCE, "POST", eviction, null, "eviction"); return true; } catch (KubernetesClientException e) { if (e.getCode() != HTTP_TOO_MANY_REQUESTS) { throw e; } return false; - } catch (IOException exception) { - throw KubernetesClientException.launderThrowable(forOperationType("evict"), exception); - } catch (InterruptedException interruptedException) { - Thread.currentThread().interrupt(); - throw KubernetesClientException.launderThrowable(forOperationType("evict"), interruptedException); } } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/BaseClient.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/BaseClient.java index 8ff5dd2033..0fecd1d531 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/BaseClient.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/BaseClient.java @@ -30,8 +30,10 @@ import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.VersionInfo; import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.NamespacedInOutCreateable; import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.kubernetes.client.dsl.base.ResourceDefinitionContext; +import io.fabric8.kubernetes.client.dsl.internal.CreateOnlyResourceOperationsImpl; import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperationsImpl; import io.fabric8.kubernetes.client.dsl.internal.OperationContext; import io.fabric8.kubernetes.client.dsl.internal.OperationSupport; @@ -287,6 +289,13 @@ public , R extends Re } } + @Override + public NamespacedInOutCreateable createOnlyResources( + Class inType, Class outType) { + return new CreateOnlyResourceOperationsImpl<>(this, ResourceDefinitionContext.fromResourceType(inType), inType, + outType); + } + public > HasMetadataOperationsImpl newHasMetadataOperation( ResourceDefinitionContext rdContext, Class resourceType, Class listClass) { return new HasMetadataOperationsImpl<>(this, rdContext, resourceType, listClass); diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/Handlers.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/Handlers.java index 66c5586880..6a9dd44b4f 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/Handlers.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/Handlers.java @@ -24,6 +24,7 @@ import io.fabric8.kubernetes.api.model.KubernetesResourceList; import io.fabric8.kubernetes.client.Client; import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.dsl.InOutCreateable; import io.fabric8.kubernetes.client.dsl.NamespacedInOutCreateable; import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.kubernetes.client.dsl.base.ResourceDefinitionContext; @@ -57,7 +58,7 @@ public void unregister(Class type) { /** * Returns a {@link ResourceHandler} for the given item. The client is optional, if it is supplied, then the server can be * consulted for resource metadata if a generic item is passed in. - * + * * @return the handler * @throws KubernetesClientException if a handler cannot be found */ @@ -152,7 +153,17 @@ public , R extends Re public NamespacedInOutCreateable getNamespacedHasMetadataCreateOnlyOperation(Class type, Client client) { HasMetadataOperation> operation = getNonListingOperation(type, client); - return operation::inNamespace; + return new NamespacedInOutCreateable() { + @Override + public T create(T item) { + return operation.create(item); + } + + @Override + public InOutCreateable inNamespace(String name) { + return operation.inNamespace(name); + } + }; } } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromClusterIPImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromClusterIPImpl.java index 63f424ceaf..47b4c8ae52 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromClusterIPImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromClusterIPImpl.java @@ -19,7 +19,6 @@ import io.fabric8.kubernetes.api.model.ServicePort; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.ServiceToURLProvider; -import io.fabric8.kubernetes.client.utils.internal.URLFromServiceUtil; public class URLFromClusterIPImpl implements ServiceToURLProvider { @Override @@ -29,7 +28,7 @@ public int getPriority() { @Override public String getURL(Service service, String portName, String namespace, KubernetesClient client) { - ServicePort port = URLFromServiceUtil.getServicePortByName(service, portName); + ServicePort port = ServiceToURLProvider.getServicePortByName(service, portName); if (port != null && service.getSpec().getType().equals("ClusterIP")) { return port.getProtocol().toLowerCase() + "://" + service.getSpec().getClusterIP() + ":" + port.getPort(); } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromIngressImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromIngressImpl.java index f049e54d38..2d1c0ba7e1 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromIngressImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromIngressImpl.java @@ -27,7 +27,7 @@ public class URLFromIngressImpl implements ServiceToURLProvider { @Override public String getURL(Service service, String portName, String namespace, KubernetesClient client) { - ServicePort port = URLFromServiceUtil.getServicePortByName(service, portName); + ServicePort port = ServiceToURLProvider.getServicePortByName(service, portName); String serviceName = service.getMetadata().getName(); if (port == null) { throw new RuntimeException("Couldn't find port: " + portName + " for service " + service.getMetadata().getName()); diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromNodePortImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromNodePortImpl.java index 17a6b0f5e6..2ac13f77d0 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromNodePortImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromNodePortImpl.java @@ -20,7 +20,6 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.ServiceToURLProvider; -import io.fabric8.kubernetes.client.utils.internal.URLFromServiceUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,7 +30,7 @@ public class URLFromNodePortImpl implements ServiceToURLProvider { public static final Logger logger = LoggerFactory.getLogger(URLFromNodePortImpl.class); public String getURL(Service service, String portName, String namespace, KubernetesClient client) { - ServicePort port = URLFromServiceUtil.getServicePortByName(service, portName); + ServicePort port = ServiceToURLProvider.getServicePortByName(service, portName); String serviceProto = port.getProtocol(); NodePortUrlComponents urlComponents = null; Integer nodePort = port.getNodePort(); diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/URLFromServiceUtil.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/URLFromServiceUtil.java index 92be6e1bb2..45dbe3a82f 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/URLFromServiceUtil.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/URLFromServiceUtil.java @@ -16,8 +16,17 @@ package io.fabric8.kubernetes.client.utils.internal; -import io.fabric8.kubernetes.api.model.*; -import io.fabric8.kubernetes.api.model.extensions.*; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.IntOrString; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.ServicePort; +import io.fabric8.kubernetes.api.model.extensions.HTTPIngressPath; +import io.fabric8.kubernetes.api.model.extensions.HTTPIngressRuleValue; +import io.fabric8.kubernetes.api.model.extensions.Ingress; +import io.fabric8.kubernetes.api.model.extensions.IngressBackend; +import io.fabric8.kubernetes.api.model.extensions.IngressRule; +import io.fabric8.kubernetes.api.model.extensions.IngressSpec; +import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; import io.fabric8.kubernetes.client.utils.URLUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -181,23 +190,6 @@ private static boolean portsMatch(ServicePort servicePort, IntOrString intOrStri } public static String getNamespace(HasMetadata entity) { - if (entity != null) { - return entity.getMetadata() != null ? entity.getMetadata().getNamespace() : null; - } else { - return null; - } - } - - public static ServicePort getServicePortByName(Service service, String portName) { - if (portName.isEmpty()) { - return service.getSpec().getPorts().iterator().next(); - } - - for (ServicePort servicePort : service.getSpec().getPorts()) { - if (Objects.equals(servicePort.getName(), portName)) { - return servicePort; - } - } - return null; + return KubernetesResourceUtil.getNamespace(entity); } } diff --git a/kubernetes-tests/src/test/java/io/fabric8/openshift/client/server/mock/OpenshiftRoleBindingTest.java b/kubernetes-tests/src/test/java/io/fabric8/openshift/client/server/mock/OpenshiftRoleBindingTest.java index e392ffb05d..6bdceed5b6 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/openshift/client/server/mock/OpenshiftRoleBindingTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/openshift/client/server/mock/OpenshiftRoleBindingTest.java @@ -16,6 +16,8 @@ package io.fabric8.openshift.client.server.mock; import com.fasterxml.jackson.databind.ObjectMapper; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; import io.fabric8.openshift.api.model.RoleBinding; import io.fabric8.openshift.api.model.RoleBindingBuilder; import io.fabric8.openshift.client.OpenShiftClient; @@ -23,17 +25,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -@EnableOpenShiftMockClient +@EnableKubernetesMockClient class OpenshiftRoleBindingTest { - OpenShiftMockServer server; + KubernetesMockServer server; OpenShiftClient client; private RoleBinding expectedRoleBinding = new RoleBindingBuilder() .withNewMetadata() + .withName("testrb") .endMetadata() - .addToUserNames("testuser1", "testuser2", "system:serviceaccount:test:svcacct") - .addToGroupNames("testgroup") .addNewSubject() .withKind("User") .withName("testuser1") @@ -44,7 +45,6 @@ class OpenshiftRoleBindingTest { .endSubject() .addNewSubject() .withKind("ServiceAccount") - .withNamespace("test") .withName("svcacct") .endSubject() .addNewSubject() @@ -62,9 +62,10 @@ void testCreateWithOnlySubjects() throws Exception { .once(); RoleBinding response = client.roleBindings() - .create( + .resource( new RoleBindingBuilder() .withNewMetadata() + .withName("testrb") .endMetadata() .addNewSubject() .withKind("User") @@ -82,60 +83,14 @@ void testCreateWithOnlySubjects() throws Exception { .withKind("Group") .withName("testgroup") .endSubject() - .build()); + .build()) + .create(); assertEquals(expectedRoleBinding, response); assertEquals(expectedRoleBinding, new ObjectMapper().readerFor(RoleBinding.class).readValue(server.getLastRequest().getBody().readByteArray())); } - @Test - void testCreateWithUserNamesAndGroupsAndNoSubjects() throws Exception { - server.expect() - .post() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings") - .andReturn(201, expectedRoleBinding) - .once(); - - RoleBinding response = client.roleBindings() - .create( - new RoleBindingBuilder() - .withNewMetadata() - .endMetadata() - .addToUserNames("testuser1", "testuser2", "system:serviceaccount:test:svcacct") - .addToGroupNames("testgroup") - .build()); - assertEquals(expectedRoleBinding, response); - assertEquals(expectedRoleBinding, - new ObjectMapper().readerFor(RoleBinding.class).readValue(server.getLastRequest().getBody().inputStream())); - } - - @Test - void testCreateWithUserNamesAndGroupsAndOverwriteSubjects() throws Exception { - server.expect() - .post() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings") - .andReturn(201, expectedRoleBinding) - .once(); - - RoleBinding response = client.roleBindings() - .create( - new RoleBindingBuilder() - .withNewMetadata() - .endMetadata() - .addToUserNames("testuser1", "testuser2", "system:serviceaccount:test:svcacct") - .addToGroupNames("testgroup") - .addNewSubject() - .withKind("User") - .withName("unexpected") - .endSubject() - .build()); - assertEquals(expectedRoleBinding, response); - - assertEquals(expectedRoleBinding, - new ObjectMapper().readerFor(RoleBinding.class).readValue(server.getLastRequest().getBody().inputStream())); - } - @Test void testReplaceWithOnlySubjects() throws Exception { server.expect() @@ -150,10 +105,10 @@ void testReplaceWithOnlySubjects() throws Exception { .once(); RoleBinding response = client.roleBindings() - .withName("testrb") - .replace( + .resource( new RoleBindingBuilder() .withNewMetadata() + .withName("testrb") .endMetadata() .addNewSubject() .withKind("User") @@ -171,210 +126,12 @@ void testReplaceWithOnlySubjects() throws Exception { .withKind("Group") .withName("testgroup") .endSubject() - .build()); + .build()) + .replace(); assertEquals(expectedRoleBinding, response); assertEquals(expectedRoleBinding, new ObjectMapper().readerFor(RoleBinding.class).readValue(server.getLastRequest().getBody().inputStream())); } - @Test - void testReplaceWithUserNamesAndGroupsAndNoSubjects() throws Exception { - server.expect() - .get() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings/testrb") - .andReturn(200, expectedRoleBinding) - .once(); - server.expect() - .put() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings/testrb") - .andReturn(200, expectedRoleBinding) - .once(); - - RoleBinding response = client.roleBindings() - .withName("testrb") - .replace( - new RoleBindingBuilder() - .withNewMetadata() - .endMetadata() - .addToUserNames("testuser1", "testuser2", "system:serviceaccount:test:svcacct") - .addToGroupNames("testgroup") - .build()); - assertEquals(expectedRoleBinding, response); - - assertEquals(expectedRoleBinding, - new ObjectMapper().readerFor(RoleBinding.class).readValue(server.getLastRequest().getBody().inputStream())); - } - - @Test - void testReplaceWithUserNamesAndGroupsAndOverwriteSubjects() throws Exception { - server.expect() - .get() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings/testrb") - .andReturn(200, expectedRoleBinding) - .once(); - server.expect() - .put() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings/testrb") - .andReturn(200, expectedRoleBinding) - .once(); - - RoleBinding response = client.roleBindings() - .withName("testrb") - .replace( - new RoleBindingBuilder() - .withNewMetadata() - .endMetadata() - .addToUserNames("testuser1", "testuser2", "system:serviceaccount:test:svcacct") - .addToGroupNames("testgroup") - .addNewSubject() - .withKind("User") - .withName("unexpected") - .endSubject() - .build()); - assertEquals(expectedRoleBinding, response); - - assertEquals(expectedRoleBinding, - new ObjectMapper().readerFor(RoleBinding.class).readValue(server.getLastRequest().getBody().inputStream())); - } - - @Test - void testPatchWithOnlySubjects() throws Exception { - server.expect() - .get() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings/testrb") - .andReturn(200, new RoleBindingBuilder().addToUserNames("unexpected").build()) - .once(); - server.expect() - .patch() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings/testrb") - .andReturn(200, expectedRoleBinding) - .once(); - - RoleBinding response = client.roleBindings() - .withName("testrb") - .patch( - new RoleBindingBuilder() - .withNewMetadata() - .endMetadata() - .addNewSubject() - .withKind("User") - .withName("testuser1") - .endSubject() - .addNewSubject() - .withKind("User") - .withName("testuser2") - .endSubject() - .addNewSubject() - .withKind("ServiceAccount") - .withName("svcacct") - .endSubject() - .addNewSubject() - .withKind("Group") - .withName("testgroup") - .endSubject() - .build()); - assertEquals(expectedRoleBinding, response); - - assertEquals( - "[{\"op\":\"replace\",\"path\":\"/userNames/0\",\"value\":\"testuser1\"},{\"op\":\"add\",\"path\":\"/userNames/1\",\"value\":\"testuser2\"},{\"op\":\"add\",\"path\":\"/userNames/2\",\"value\":\"system:serviceaccount:test:svcacct\"},{\"op\":\"add\",\"path\":\"/metadata\",\"value\":{}},{\"op\":\"add\",\"path\":\"/groupNames\",\"value\":[\"testgroup\"]},{\"op\":\"add\",\"path\":\"/subjects\",\"value\":[{\"kind\":\"User\",\"name\":\"testuser1\"},{\"kind\":\"User\",\"name\":\"testuser2\"},{\"kind\":\"ServiceAccount\",\"name\":\"svcacct\",\"namespace\":\"test\"},{\"kind\":\"Group\",\"name\":\"testgroup\"}]}]", - server.getLastRequest().getBody().readUtf8()); - } - - @Test - void testPatchWithUserNamesAndGroupsAndNoSubjects() throws Exception { - server.expect() - .get() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings/testrb") - .andReturn(200, new RoleBindingBuilder().addToUserNames("unexpected").build()) - .once(); - server.expect() - .patch() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings/testrb") - .andReturn(200, expectedRoleBinding) - .once(); - - RoleBinding response = client.roleBindings() - .withName("testrb") - .patch( - new RoleBindingBuilder() - .withNewMetadata() - .endMetadata() - .addToUserNames("testuser1", "testuser2", "system:serviceaccount:test:svcacct") - .addToGroupNames("testgroup") - .build()); - assertEquals(expectedRoleBinding, response); - - assertEquals( - "[{\"op\":\"replace\",\"path\":\"/userNames/0\",\"value\":\"testuser1\"},{\"op\":\"add\",\"path\":\"/userNames/1\",\"value\":\"testuser2\"},{\"op\":\"add\",\"path\":\"/userNames/2\",\"value\":\"system:serviceaccount:test:svcacct\"},{\"op\":\"add\",\"path\":\"/metadata\",\"value\":{}},{\"op\":\"add\",\"path\":\"/groupNames\",\"value\":[\"testgroup\"]},{\"op\":\"add\",\"path\":\"/subjects\",\"value\":[{\"kind\":\"User\",\"name\":\"testuser1\"},{\"kind\":\"User\",\"name\":\"testuser2\"},{\"kind\":\"ServiceAccount\",\"name\":\"svcacct\",\"namespace\":\"test\"},{\"kind\":\"Group\",\"name\":\"testgroup\"}]}]", - server.getLastRequest().getBody().readUtf8()); - } - - @Test - void testPatchWithUserNamesAndGroupsAndOverwriteSubjects() throws Exception { - server.expect() - .get() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings/testrb") - .andReturn(200, new RoleBindingBuilder().addToUserNames("unexpected").build()) - .once(); - server.expect() - .patch() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings/testrb") - .andReturn(200, expectedRoleBinding) - .once(); - - RoleBinding response = client.roleBindings() - .withName("testrb") - .patch( - new RoleBindingBuilder() - .withNewMetadata() - .endMetadata() - .addToUserNames("testuser1", "testuser2", "system:serviceaccount:test:svcacct") - .addToGroupNames("testgroup") - .addNewSubject() - .withKind("User") - .withName("unexpected") - .endSubject() - .build()); - assertEquals(expectedRoleBinding, response); - - assertEquals( - "[{\"op\":\"replace\",\"path\":\"/userNames/0\",\"value\":\"testuser1\"},{\"op\":\"add\",\"path\":\"/userNames/1\",\"value\":\"testuser2\"},{\"op\":\"add\",\"path\":\"/userNames/2\",\"value\":\"system:serviceaccount:test:svcacct\"},{\"op\":\"add\",\"path\":\"/metadata\",\"value\":{}},{\"op\":\"add\",\"path\":\"/groupNames\",\"value\":[\"testgroup\"]},{\"op\":\"add\",\"path\":\"/subjects\",\"value\":[{\"kind\":\"User\",\"name\":\"testuser1\"},{\"kind\":\"User\",\"name\":\"testuser2\"},{\"kind\":\"ServiceAccount\",\"name\":\"svcacct\",\"namespace\":\"test\"},{\"kind\":\"Group\",\"name\":\"testgroup\"}]}]", - server.getLastRequest().getBody().readUtf8()); - } - - @Test - void testCreateInline() throws Exception { - server.expect() - .post() - .withPath("/apis/authorization.openshift.io/v1/namespaces/test/rolebindings") - .andReturn(201, expectedRoleBinding) - .once(); - - RoleBinding response = client.roleBindings() - .create(new RoleBindingBuilder() - .withNewMetadata() - .endMetadata() - .addNewSubject() - .withKind("User") - .withName("testuser1") - .endSubject() - .addNewSubject() - .withKind("User") - .withName("testuser2") - .endSubject() - .addNewSubject() - .withKind("ServiceAccount") - .withName("svcacct") - .endSubject() - .addNewSubject() - .withKind("Group") - .withName("testgroup") - .endSubject() - .build()); - assertEquals(expectedRoleBinding, response); - assertEquals(expectedRoleBinding, - new ObjectMapper().readerFor(RoleBinding.class).readValue(server.getLastRequest().getBody().inputStream())); - } - } diff --git a/openshift-client/pom.xml b/openshift-client/pom.xml index e084f917f5..e2d9469f07 100644 --- a/openshift-client/pom.xml +++ b/openshift-client/pom.xml @@ -110,7 +110,6 @@ org.slf4j slf4j-simple - ${slf4j.version} test diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/authorization/RoleBindingOperationsImpl.java b/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/authorization/RoleBindingOperationsImpl.java deleted file mode 100644 index ade8da83be..0000000000 --- a/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/authorization/RoleBindingOperationsImpl.java +++ /dev/null @@ -1,136 +0,0 @@ -/** - * 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.dsl.internal.authorization; - -import io.fabric8.kubernetes.api.builder.TypedVisitor; -import io.fabric8.kubernetes.api.model.ObjectReference; -import io.fabric8.kubernetes.api.model.ObjectReferenceBuilder; -import io.fabric8.kubernetes.client.Client; -import io.fabric8.kubernetes.client.dsl.Resource; -import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperation; -import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperationsImpl; -import io.fabric8.kubernetes.client.dsl.internal.OperationContext; -import io.fabric8.openshift.api.model.RoleBinding; -import io.fabric8.openshift.api.model.RoleBindingBuilder; -import io.fabric8.openshift.api.model.RoleBindingList; - -import java.io.IOException; -import java.util.List; -import java.util.function.Supplier; - -import static io.fabric8.openshift.client.OpenShiftAPIGroups.AUTHORIZATION; - -public class RoleBindingOperationsImpl extends HasMetadataOperation> { - - public static final String SERVICE_ACCOUNT = "ServiceAccount"; - public static final String USER = "User"; - public static final String GROUP = "Group"; - - public RoleBindingOperationsImpl(Client client) { - this(HasMetadataOperationsImpl.defaultContext(client)); - } - - public RoleBindingOperationsImpl(OperationContext context) { - super(context.withApiGroupName(AUTHORIZATION) - .withPlural("rolebindings"), RoleBinding.class, RoleBindingList.class); - } - - @Override - public RoleBindingOperationsImpl newInstance(OperationContext context) { - return new RoleBindingOperationsImpl(context); - } - - @Override - protected RoleBinding handleCreate(RoleBinding resource) throws InterruptedException, IOException { - return super.handleCreate(enrichRoleBinding(resource)); - } - - @Override - protected RoleBinding modifyItemForReplaceOrPatch(Supplier current, RoleBinding binding) { - return enrichRoleBinding(binding); - } - - private RoleBinding enrichRoleBinding(RoleBinding binding) { - RoleBindingBuilder builder = new RoleBindingBuilder(binding); - - if ((binding.getUserNames() != null && !binding.getUserNames().isEmpty()) || - (binding.getGroupNames() != null && !binding.getGroupNames().isEmpty())) { - enrichFromUsersAndGroups(builder, binding.getUserNames(), binding.getGroupNames()); - } else { - enrichFromSubjects(builder, binding.getSubjects()); - enrichSubjectsNamespace(builder); - } - - return builder.build(); - } - - private void enrichSubjectsNamespace(RoleBindingBuilder builder) { - builder.accept(new TypedVisitor() { - @Override - public void visit(ObjectReferenceBuilder o) { - if (o.getKind() != null && o.getKind().equals(SERVICE_ACCOUNT) - && (o.getNamespace() == null || o.getNamespace().isEmpty())) { - o.withNamespace(getNamespace()); - } - } - }); - } - - private void enrichFromUsersAndGroups(RoleBindingBuilder builder, List userNames, List groupNames) { - builder.withSubjects(); - - if (userNames != null) { - for (String userName : userNames) { - if (userName.startsWith("system:serviceaccount:")) { - String[] splitUserName = userName.split(":"); - if (splitUserName.length == 4) { - builder.addNewSubject().withKind(SERVICE_ACCOUNT).withNamespace(splitUserName[2]).withName(splitUserName[3]) - .endSubject(); - continue; - } - } - - builder.addNewSubject().withKind(USER).withName(userName).endSubject(); - } - } - - if (groupNames != null) { - for (String groupName : groupNames) { - builder.addNewSubject().withKind(GROUP).withName(groupName).endSubject(); - } - } - } - - private void enrichFromSubjects(RoleBindingBuilder builder, List subjects) { - for (ObjectReference ref : subjects) { - switch (ref.getKind()) { - case USER: - builder.addToUserNames(ref.getName()); - break; - case SERVICE_ACCOUNT: - String namespace = ref.getNamespace(); - if (namespace == null || namespace.isEmpty()) { - namespace = getNamespace(); - } - builder.addToUserNames("system:serviceaccount:" + namespace + ":" + ref.getName()); - break; - case GROUP: - builder.addToGroupNames(ref.getName()); - break; - } - } - } -} diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/project/ProjectOperationsImpl.java b/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/project/ProjectOperationsImpl.java index 9d68bb9039..87bf780cf6 100644 --- a/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/project/ProjectOperationsImpl.java +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/dsl/internal/project/ProjectOperationsImpl.java @@ -19,11 +19,10 @@ import io.fabric8.kubernetes.api.model.rbac.RoleBinding; import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder; import io.fabric8.kubernetes.client.Client; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.dsl.MixedOperation; import io.fabric8.kubernetes.client.dsl.Resource; -import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperation; -import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperationsImpl; -import io.fabric8.kubernetes.client.dsl.internal.NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl; -import io.fabric8.kubernetes.client.dsl.internal.OperationContext; +import io.fabric8.kubernetes.client.extension.ExtensibleMixedOperation; import io.fabric8.openshift.api.model.Project; import io.fabric8.openshift.api.model.ProjectBuilder; import io.fabric8.openshift.api.model.ProjectList; @@ -32,33 +31,18 @@ import java.util.ArrayList; import java.util.List; -import static io.fabric8.openshift.client.OpenShiftAPIGroups.PROJECT; - -public class ProjectOperationsImpl extends HasMetadataOperation> +public class ProjectOperationsImpl extends ExtensibleMixedOperation> implements ProjectOperation { + public static final String OPENSHIFT_IO_DESCRIPTION_ANNOTATION = "openshift.io/description"; public static final String OPENSHIFT_IO_DISPLAY_NAME_ANNOTATION = "openshift.io/display-name"; public static final String OPENSHIFT_IO_REQUESTER_ANNOTATION = "openshift.io/requester"; public static final String RBAC_AUTHORIZATION_APIGROUP = "rbac.authorization.k8s.io"; public static final String CLUSTER_ROLE = "ClusterRole"; - public ProjectOperationsImpl(Client client) { - this(HasMetadataOperationsImpl.defaultContext(client)); - } - - public ProjectOperationsImpl(OperationContext context) { - super(context.withApiGroupName(PROJECT) - .withPlural("projects"), Project.class, ProjectList.class); - } - - @Override - public ProjectOperationsImpl newInstance(OperationContext context) { - return new ProjectOperationsImpl(context); - } - - @Override - public boolean isResourceNamespaced() { - return false; + public ProjectOperationsImpl(Client client, + MixedOperation> resources) { + super(client, resources); } @Override @@ -72,9 +56,7 @@ public List createProjectAndRoleBindings(String name, String descri result.add(create(project)); // Create Role Bindings - NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl listOp = new NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl( - context.withItem(projectRoleBindings)); - result.addAll(listOp.createOrReplace()); + result.addAll(this.client.adapt(KubernetesClient.class).resourceList(projectRoleBindings).createOrReplace()); return result; } @@ -173,4 +155,4 @@ private List initRoleBindings(String name, String adminUser) { return resources; } -} +} \ No newline at end of file diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftClientImpl.java b/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftClientImpl.java index 3931bbdc25..630854cdc1 100644 --- a/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftClientImpl.java +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftClientImpl.java @@ -98,6 +98,8 @@ import io.fabric8.openshift.api.model.PodSecurityPolicyReview; import io.fabric8.openshift.api.model.PodSecurityPolicySelfSubjectReview; import io.fabric8.openshift.api.model.PodSecurityPolicySubjectReview; +import io.fabric8.openshift.api.model.Project; +import io.fabric8.openshift.api.model.ProjectList; import io.fabric8.openshift.api.model.ProjectRequest; import io.fabric8.openshift.api.model.RangeAllocation; import io.fabric8.openshift.api.model.RangeAllocationList; @@ -164,7 +166,6 @@ import io.fabric8.openshift.client.dsl.ProjectRequestOperation; import io.fabric8.openshift.client.dsl.TemplateResource; import io.fabric8.openshift.client.dsl.internal.apps.DeploymentConfigOperationsImpl; -import io.fabric8.openshift.client.dsl.internal.authorization.RoleBindingOperationsImpl; import io.fabric8.openshift.client.dsl.internal.build.BuildConfigOperationsImpl; import io.fabric8.openshift.client.dsl.internal.build.BuildOperationsImpl; import io.fabric8.openshift.client.dsl.internal.core.TemplateOperationsImpl; @@ -415,7 +416,7 @@ public NonNamespaceOperation @Override public NamespacedInOutCreateable podSecurityPolicyReviews() { - return OpenShiftHandlers.getCreateOnlyResourceOperation(PodSecurityPolicyReview.class, PodSecurityPolicyReview.class, this); + return createOnlyResources(PodSecurityPolicyReview.class, PodSecurityPolicyReview.class); } @Override @@ -435,19 +436,17 @@ public MixedOperation> eg @Override public NamespacedInOutCreateable podSecurityPolicySelfSubjectReviews() { - return OpenShiftHandlers.getCreateOnlyResourceOperation(PodSecurityPolicySelfSubjectReview.class, - PodSecurityPolicySelfSubjectReview.class, this); + return createOnlyResources(PodSecurityPolicySelfSubjectReview.class, PodSecurityPolicySelfSubjectReview.class); } @Override public NamespacedInOutCreateable podSecurityPolicySubjectReviews() { - return OpenShiftHandlers.getCreateOnlyResourceOperation(PodSecurityPolicySubjectReview.class, - PodSecurityPolicySubjectReview.class, this); + return createOnlyResources(PodSecurityPolicySubjectReview.class, PodSecurityPolicySubjectReview.class); } @Override public ProjectOperation projects() { - return new ProjectOperationsImpl(this); + return new ProjectOperationsImpl(this, this.resources(Project.class, ProjectList.class)); } @Override @@ -480,7 +479,7 @@ public MixedOperation> roles() { @Override public MixedOperation> roleBindings() { - return new RoleBindingOperationsImpl(this); + return resources(RoleBinding.class, RoleBindingList.class); } @Override @@ -637,35 +636,32 @@ public MixedOperation> @Override public InOutCreateable subjectAccessReviews() { - return OpenShiftHandlers.getCreateOnlyResourceOperation(SubjectAccessReview.class, SubjectAccessReviewResponse.class, this); + return createOnlyResources(SubjectAccessReview.class, SubjectAccessReviewResponse.class); } @Override public InOutCreateable resourceAccessReviews() { - return OpenShiftHandlers.getCreateOnlyResourceOperation(ResourceAccessReview.class, ResourceAccessReviewResponse.class, - this); + return createOnlyResources(ResourceAccessReview.class, ResourceAccessReviewResponse.class); } @Override public NamespacedInOutCreateable localSubjectAccessReviews() { - return OpenShiftHandlers.getCreateOnlyResourceOperation(LocalSubjectAccessReview.class, SubjectAccessReviewResponse.class, - this); + return createOnlyResources(LocalSubjectAccessReview.class, SubjectAccessReviewResponse.class); } @Override public NamespacedInOutCreateable localResourceAccessReviews() { - return OpenShiftHandlers.getCreateOnlyResourceOperation(LocalResourceAccessReview.class, ResourceAccessReviewResponse.class, - this); + return createOnlyResources(LocalResourceAccessReview.class, ResourceAccessReviewResponse.class); } @Override public NamespacedInOutCreateable selfSubjectRulesReviews() { - return OpenShiftHandlers.getCreateOnlyResourceOperation(SelfSubjectRulesReview.class, SelfSubjectRulesReview.class, this); + return createOnlyResources(SelfSubjectRulesReview.class, SelfSubjectRulesReview.class); } @Override public NamespacedInOutCreateable subjectRulesReviews() { - return OpenShiftHandlers.getCreateOnlyResourceOperation(SubjectRulesReview.class, SubjectRulesReview.class, this); + return createOnlyResources(SubjectRulesReview.class, SubjectRulesReview.class); } @Override diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftExtensionAdapter.java b/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftExtensionAdapter.java index 0405492db6..54c22fb236 100644 --- a/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftExtensionAdapter.java +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftExtensionAdapter.java @@ -23,8 +23,6 @@ import io.fabric8.openshift.api.model.Build; import io.fabric8.openshift.api.model.BuildConfig; import io.fabric8.openshift.api.model.DeploymentConfig; -import io.fabric8.openshift.api.model.Project; -import io.fabric8.openshift.api.model.RoleBinding; import io.fabric8.openshift.api.model.Template; import io.fabric8.openshift.client.OpenShiftClient; import io.fabric8.openshift.client.dsl.MachineConfigurationAPIGroupDSL; @@ -43,11 +41,9 @@ import io.fabric8.openshift.client.dsl.V1ClusterAutoscalingAPIGroupDSL; import io.fabric8.openshift.client.dsl.V1beta1ClusterAutoscalingAPIGroupDSL; import io.fabric8.openshift.client.dsl.internal.apps.DeploymentConfigOperationsImpl; -import io.fabric8.openshift.client.dsl.internal.authorization.RoleBindingOperationsImpl; import io.fabric8.openshift.client.dsl.internal.build.BuildConfigOperationsImpl; import io.fabric8.openshift.client.dsl.internal.build.BuildOperationsImpl; import io.fabric8.openshift.client.dsl.internal.core.TemplateOperationsImpl; -import io.fabric8.openshift.client.dsl.internal.project.ProjectOperationsImpl; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Service; @@ -70,9 +66,7 @@ public void registerHandlers(Handlers handlers) { handlers.register(BuildConfig.class, BuildConfigOperationsImpl::new); handlers.register(Build.class, BuildOperationsImpl::new); handlers.register(DeploymentConfig.class, DeploymentConfigOperationsImpl::new); - handlers.register(RoleBinding.class, RoleBindingOperationsImpl::new); handlers.register(Template.class, TemplateOperationsImpl::new); - handlers.register(Project.class, ProjectOperationsImpl::new); } @Override diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftHandlers.java b/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftHandlers.java deleted file mode 100644 index e3908274d2..0000000000 --- a/openshift-client/src/main/java/io/fabric8/openshift/client/impl/OpenShiftHandlers.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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.impl; - -import io.fabric8.kubernetes.api.model.KubernetesResource; -import io.fabric8.kubernetes.client.Client; -import io.fabric8.kubernetes.client.dsl.base.ResourceDefinitionContext; -import io.fabric8.kubernetes.client.dsl.internal.CreateOnlyResourceOperationsImpl; - -public final class OpenShiftHandlers { - - private OpenShiftHandlers() { - // Utility - } - - public static CreateOnlyResourceOperationsImpl getCreateOnlyResourceOperation( - Class inputType, Class outputType, Client client) { - return new CreateOnlyResourceOperationsImpl<>(client, ResourceDefinitionContext.fromResourceType(inputType), inputType, - outputType); - } - -} diff --git a/openshift-client/src/main/java/io/fabric8/openshift/client/impl/URLFromOpenshiftRouteImpl.java b/openshift-client/src/main/java/io/fabric8/openshift/client/impl/URLFromOpenshiftRouteImpl.java index 937ab0b2ed..b881cab64f 100644 --- a/openshift-client/src/main/java/io/fabric8/openshift/client/impl/URLFromOpenshiftRouteImpl.java +++ b/openshift-client/src/main/java/io/fabric8/openshift/client/impl/URLFromOpenshiftRouteImpl.java @@ -21,7 +21,6 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.ServiceToURLProvider; -import io.fabric8.kubernetes.client.utils.internal.URLFromServiceUtil; import io.fabric8.openshift.api.model.Route; import io.fabric8.openshift.client.OpenShiftClient; import org.slf4j.Logger; @@ -36,7 +35,7 @@ public class URLFromOpenshiftRouteImpl implements ServiceToURLProvider { @Override public String getURL(Service service, String portName, String namespace, KubernetesClient client) { String serviceName = service.getMetadata().getName(); - ServicePort port = URLFromServiceUtil.getServicePortByName(service, portName); + ServicePort port = ServiceToURLProvider.getServicePortByName(service, portName); if (port != null && port.getName() != null && client.isAdaptable(OpenShiftClient.class)) { try { String serviceProtocol = port.getProtocol();