Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[DESIGN] Add k8s mock client and server test case #956

Closed
cdmikechen opened this issue May 15, 2022 · 1 comment
Closed

[DESIGN] Add k8s mock client and server test case #956

cdmikechen opened this issue May 15, 2022 · 1 comment

Comments

@cdmikechen
Copy link
Contributor

cdmikechen commented May 15, 2022

At present, submarine's tests on k8s services are mainly in submarine-k8s packages. Because k8s service is a complex service composition, it is difficult to write unit tests of many k8s related functions (like submarine-server-submitter package) in a simple way.

I think that since most of the methods in submarine-server-submitter package are processed by k8s API similar to CRUD, it should be necessary to keep/create enough test cases to test and verify these methods. I have just search some k8s client related test cases informations. In fact, I think the main problem is how to support a mock server, so that we can break away from the constraints of k8s and write test cases freely.
I think in this issue kubernetes-client/java#1842 we can get some inspiration. Most of k8s apis can simulate the return messages based on the http request, so we can refer to the methods mentioned in it to change the existing test cases.

Mock Client and Server

I've write a test case about how to test k8s client by a mock client and server (WireMockRule). The core of the whole test case is to use fake message to response the client request.
It refers to k8s java official test case

The following codes can be found in https://github.com/shangyuantech/submarine/blob/SUBMARINE-1174/submarine-server/server-submitter/submitter-k8s/src/test/java/org/apache/submarine/server/submitter/k8s/SubmitterK8sMockApiTest.java

public class SubmitterK8sMockApiTest {

  private K8sClient k8sClient;

  @Rule
  public WireMockRule wireMockRule = K8sMockClient.getWireMockRule();

  @Before
  public void setup() throws IOException {
    this.k8sClient = new K8sMockClient(post(urlPathEqualTo("/api/v1/namespaces/foo/configmaps"))
            .withHeader("Content-Type", new EqualToPattern("application/json; charset=UTF-8"))
            .willReturn(
                    aResponse()
                            .withStatus(200)
                            .withBody("{\"metadata\":{\"name\":\"bar\",\"namespace\":\"foo\"}}")));
  }

  @Test
  public void testApplyConfigMap() {
    V1ConfigMap configMap = new V1ConfigMap()
            .apiVersion("v1")
            .metadata(new V1ObjectMeta().namespace("foo").name("bar"))
            .data(Collections.singletonMap("key1", "value1"));

    KubernetesApiResponse<V1ConfigMap> configMapResp = k8sClient.getConfigMapClient().create(configMap);
    V1ConfigMap rtnConfigmap = configMapResp.getObject();

    assertNotNull(rtnConfigmap);
    assertEquals(rtnConfigmap.getMetadata().getNamespace(), "foo");
    assertEquals(rtnConfigmap.getMetadata().getName(), "bar");
  }
}

In this case, I create a K8sMockClient. In this class I did some initialization of URL and WireMockRule for the client.
The following codes can be found in https://github.com/shangyuantech/submarine/blob/SUBMARINE-1174/submarine-server/server-submitter/submitter-k8s/src/test/java/org/apache/submarine/server/submitter/k8s/client/K8sMockClient.java

private static String getResourceFileContent(String resource) {
    File file = new File(Objects.requireNonNull(
            K8sMockClient.class.getClassLoader().getResource(resource)).getPath()
    );
    try {
      return new String(Files.readAllBytes(Paths.get(file.toString())));
    } catch (IOException e) {
      e.printStackTrace();
      return null;
    }
  }

  private static final String DISCOVERY_API = getResourceFileContent("client/discovery-api.json");
  private static final String DISCOVERY_APIV1 = getResourceFileContent("client/discovery-api-v1.json");
  private static final String DISCOVERY_APIS = getResourceFileContent("client/discovery-apis.json");

  private static final WireMockRule wireMockRule = new WireMockRule(8384);

  public static WireMockRule getWireMockRule() {
    return wireMockRule;
  }

  public K8sMockClient() throws IOException {
    apiClient = new ClientBuilder().setBasePath("http://localhost:" + 8384).build();
    wireMockRule.stubFor(
            get(urlPathEqualTo("/api"))
                    .willReturn(
                            aResponse()
                                    .withStatus(200)
                                    .withBody(DISCOVERY_API)));
    wireMockRule.stubFor(
            get(urlPathEqualTo("/apis"))
                    .willReturn(
                            aResponse()
                                    .withStatus(200)
                                    .withBody(DISCOVERY_APIS)));
    wireMockRule.stubFor(
            get(urlPathEqualTo("/api/v1"))
                    .willReturn(
                            aResponse()
                                    .withStatus(200)
                                    .withBody(DISCOVERY_APIV1)));
    coreApi = new CoreV1Api();
    appsV1Api = new AppsV1Api();
    customObjectsApi = new CustomObjectsApi();
    configMapClient =
            new GenericKubernetesApi<>(
                    V1ConfigMap.class, V1ConfigMapList.class,
                    "", "v1", "configmaps", apiClient);
  }

  public K8sMockClient(MappingBuilder... mappingBuilders) throws IOException {
    this();
    addMappingBuilders(mappingBuilders);
  }

  public void addMappingBuilders(MappingBuilder... mappingBuilders) {
    // add MappingBuilder to WireMockRule
    for (MappingBuilder mappingBuilder : mappingBuilders) {
      wireMockRule.stubFor(mappingBuilder);
    }
  }

When we add a new test case, we can first confirm the response information (usually the JSON information of the resource) according to the resources we need, and then define and register the URL, RequestMethod, ResponseCode and ResponseBody into WireMockRule.

@cdmikechen
Copy link
Contributor Author

This issue has been provided in the test cases in k8s-submitter, so this issue will be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant