Skip to content

Commit

Permalink
fix #5194: prevent NPEs when the APIResourceList is missing
Browse files Browse the repository at this point in the history
  • Loading branch information
shawkins authored and manusa committed Jun 15, 2023
1 parent 8573932 commit 3e5ae25
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### 6.8-SNAPSHOT

#### Bugs
* Fix #5194: prevented NPEs due to timing issues with KubernetesClient.visitResources
* Fix #5214: null values are omitted by default, which means custom resources by in large won't need JsonIncludes annotations. The only time that is required is when null is to be preserved via @JsonInclude(value = Include.ALWAYS) on the relevant field.
* Fix #5218: No export for `io.fabric8.tekton.triggers.internal.knative.pkg.apis.duck.v1beta1` in tekton v1beta1 triggers model
* Fix #5224: Ensuring jetty sets the User-Agent header
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

import io.fabric8.kubernetes.api.model.APIGroup;
import io.fabric8.kubernetes.api.model.APIGroupBuilder;
import io.fabric8.kubernetes.api.model.APIGroupList;
import io.fabric8.kubernetes.api.model.APIResource;
import io.fabric8.kubernetes.api.model.APIResourceList;
import io.fabric8.kubernetes.api.model.APIService;
import io.fabric8.kubernetes.api.model.APIServiceList;
import io.fabric8.kubernetes.api.model.Binding;
Expand Down Expand Up @@ -160,6 +162,8 @@
import io.fabric8.kubernetes.client.utils.ApiVersionUtil;
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
import io.fabric8.kubernetes.client.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.util.ArrayList;
Expand All @@ -173,6 +177,8 @@
*/
public class KubernetesClientImpl extends BaseClient implements NamespacedKubernetesClient {

public static final Logger logger = LoggerFactory.getLogger(KubernetesClientImpl.class);

public static final String KUBERNETES_VERSION_ENDPOINT = "version";

/**
Expand Down Expand Up @@ -730,7 +736,10 @@ public void visitResources(ApiVisitor visitor) {
.withVersions(new GroupVersionForDiscoveryBuilder().withGroupVersion("v1").build()).build()))) {
return; // user terminated
}
visitGroups(visitor, getApiGroups().getGroups());
APIGroupList apiGroups = getApiGroups();
if (apiGroups != null) {
visitGroups(visitor, apiGroups.getGroups());
}
}

private boolean visitGroups(ApiVisitor visitor, List<APIGroup> groups) {
Expand All @@ -752,7 +761,14 @@ private boolean visitGroups(ApiVisitor visitor, List<APIGroup> groups) {
case SKIP:
continue;
case CONTINUE:
for (APIResource resource : this.getApiResources(groupVersion).getResources()) {
APIResourceList apiResources = this.getApiResources(groupVersion);
if (apiResources == null) {
if (logger.isDebugEnabled()) {
logger.debug("{} is discoverable, but is not yet populating an APIResource list", groupVersion);
}
continue;
}
for (APIResource resource : apiResources.getResources()) {
if (resource.getName().contains("/")) { // skip subresources
continue;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.fabric8.kubernetes.client.mock;

import io.fabric8.kubernetes.api.model.APIResource;
import io.fabric8.kubernetes.api.model.APIResourceListBuilder;
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
import io.fabric8.kubernetes.api.model.GenericKubernetesResourceList;
import io.fabric8.kubernetes.client.ApiVisitor;
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.server.mock.EnableKubernetesMockClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
import org.junit.jupiter.api.Test;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertEquals;

@EnableKubernetesMockClient
class VisitResourcesTest {
private KubernetesMockServer server;
private KubernetesClient client;

@Test
void testMissingApiResourcesAndGroups() {
// api/v1 is missing
// apis is missing
// the following should still succeed
client.visitResources(new ApiVisitor() {

@Override
public ApiVisitResult visitResource(String group, String version, APIResource apiResource,
MixedOperation<GenericKubernetesResource, GenericKubernetesResourceList, Resource<GenericKubernetesResource>> operation) {
throw new AssertionError();
}
});
}

@Test
void testVisitResource() throws Exception {
CompletableFuture<APIResource> visited = new CompletableFuture<>();
server.expect().withPath("/api/v1").andReturn(200, new APIResourceListBuilder().addNewResource()
.withName("something")
.endResource().build()).always();
// the following should still succeed
client.visitResources(new ApiVisitor() {

@Override
public ApiVisitResult visitResource(String group, String version, APIResource apiResource,
MixedOperation<GenericKubernetesResource, GenericKubernetesResourceList, Resource<GenericKubernetesResource>> operation) {
visited.complete(apiResource);
return ApiVisitResult.CONTINUE;
}
});

assertEquals("something", visited.get(0, TimeUnit.MILLISECONDS).getName());
}
}

0 comments on commit 3e5ae25

Please sign in to comment.