
# 使用kubernetes官方Java SDK访问CCI
本节将介绍如何将CCI认证工具cci-iam-authenticator与[kubernetes-client/java](https://github.com/kubernetes-client/java)结合使用以调用API。
#### 安装cci-iam-authenticator
下载安装及设置cci-iam-authenticator。
#### 安装kubernetes-client/java
详情请参考[Installation](https://github.com/kubernetes-client/java/wiki/1.-Installation) 。
![](https://support.huaweicloud.com/sdkreference-cci/public_sys-resources/note_3.0-zh-cn.png)
当前**CCI服务开放的API对应的Kubernetes版本为1.19** ，根据[Versioning-and-Compatibility](https://github.com/kubernetes-client/java/wiki/2.-Versioning-and-Compatibility)，推荐使用的SDK版本为11.0.2及以上。
如果要使用GenericKubernetesClient（参考[Code-Examples](https://github.com/kubernetes-client/java/wiki/3.-Code-Examples)），则需要9.0.0+以上版本
#### 使用Java SDK
您可以前往[开发体验馆Codelabs / Namespace生命周期代码示例（Java）](https://codelabs.developer.huaweicloud.com/codelabs/home?direction=down(is_recommend),down(published_at)&pageno=1&pagesize=18&productshorts=CCI)下载相关代码，并在线调试。
![](https://support.huaweicloud.com/sdkreference-cci/public_sys-resources/note_3.0-zh-cn.png)
示例已通过以下版本的测试：
1. 11.0.2
将以下依赖添加到项目的POM文件中：
```
<dependency>
    <groupId>io.kubernetes</groupId>
    <artifactId>client-java</artifactId>
    <version>11.0.2</version>
</dependency>
```
通过kubeconfig配置文件创建ApiClient
```
public class CommonCases {
    // ...
    public static void main(String[] args) throws IOException, ApiException, InterruptedException {
        // file path to your KubeConfig
        String kubeConfigPath = "<path to kubeconfig>";
        // loading the out-of-cluster config, a kubeconfig from file-system
        File file = new File(kubeConfigPath);
        KubeConfig config = KubeConfig.loadKubeConfig(new FileReader(file));
        config.setFile(file);
        ApiClient client = buildClient(config).build();
// ...
}
    public static ClientBuilder buildClient(KubeConfig config) throws IOException {
        final ClientBuilder builder = new ClientBuilder();
        String server = config.getServer();
        if (!server.contains("://")) {
            if (server.contains(":443")) {
                server = "https://" + server;
            } else {
                server = "http://" + server;
            }
        }
        builder.setVerifyingSsl(config.verifySSL());
        builder.setBasePath(server);
        builder.setAuthentication(new CCIKubeconfigAuthentication(config));
        return builder;
    }
}
```
CCIKubeconfigAuthentication.java
```
package com.huawei.demos;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.util.KubeConfig;
import io.kubernetes.client.util.credentials.Authentication;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.time.Instant;
/**
 * Uses a {@link KubeConfig} to configure {@link ApiClient} authentication to the CCI Kubernetes API.
 *
 * <p> Only try to use AccessTokenAuthentication mechanisms, which is enough for CCI.
 */
public class CCIKubeconfigAuthentication implements Authentication, Interceptor {
    private static final Logger log = LoggerFactory.getLogger(CCIKubeconfigAuthentication.class);
    private final KubeConfig config;
    private String token;
    private Instant expiry;
    public CCIKubeconfigAuthentication(final KubeConfig config) throws IOException {
        this.config = config;
        this.expiry = Instant.MIN;
    }
    private String getToken() {
        // get access token every 600 seconds
        if (Instant.now().isAfter(this.expiry)) {
            log.debug("Token expired, get new one from kubeconfig");
            this.token = config.getAccessToken();
            if (this.token != null) {
                this.expiry = Instant.now().plusSeconds(600);
            }
        }
        return this.token;
    }
    @Override
    public void provide(ApiClient client) {
        OkHttpClient httpClient = client.getHttpClient().newBuilder().addInterceptor(this).build();
        client.setHttpClient(httpClient);
    }
    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();
        Request authRequest;
        authRequest = request.newBuilder().header("Authorization", "Bearer " + getToken()).build();
        return chain.proceed(authRequest);
    }
}
```
访问CCI服务
```
public class CommonCases {
    // ...
    private static void createNamespace(CoreV1Api api) throws ApiException {
        String enableK8sRbac = "false";
        String flavor = "general-computing";
        String warmPoolSize = "10";
        Map<String, String> labels = new HashMap<>();
        labels.put("rbac.authorization.cci.io/enable-k8s-rbac", enableK8sRbac);
        Map<String, String> annotations = new HashMap<>();
        annotations.put("namespace.kubernetes.io/flavor", flavor);
        annotations.put("network.cci.io/warm-pool-size", warmPoolSize);
        V1Namespace namespace = new V1Namespace()
                .metadata(new V1ObjectMeta().name(NAMESPACE).labels(labels).annotations(annotations));
        LOGGER.info("start to create namespace {}", NAMESPACE);
        api.createNamespace(namespace, null, null, null);
        LOGGER.info("namespace created");
    }
    private static void createNetwork(GenericKubernetesApi<Network, NetworkList> networkApi) throws ApiException {
        String name = NETWORK;
        String projectID = "<账号ID，可以在我的凭证获取>";
        String domainID = "<项目ID，可以在我的凭证获取>";
        String securityGroupID = "<安全组ID，可以在安全组控制台获取>";
        String availableZone = "<az名称，例如cn-north-1a、cn-north-4a或cn-east-3a>";
        String vpcID = "虚拟私有云的ID，可在VPC控制台获取";
        String cidr = "<子网网段，例如192.168.128.0/18>";
        String networkID = "<子网的网络ID，可在VPC控制台 > 子网中获取>";
        String subnetID = "<子网ID，可在VPC控制台 > 子网获取>";
        String networkType = "underlay_neutron";
        Map<String, String> annotations = new HashMap<>();
        annotations.put("network.alpha.kubernetes.io/default-security-group", securityGroupID);
        annotations.put("network.alpha.kubernetes.io/domain-id", domainID);
        annotations.put("network.alpha.kubernetes.io/project-id", projectID);
        Network network = new Network()
                .metadata(new V1ObjectMeta().name(name).namespace(NAMESPACE).annotations(annotations))
                .spec(new NetworkSpec()
                        .availableZone(availableZone)
                        .cidr(cidr)
                        .attachedVPC(vpcID)
                        .networkID(networkID)
                        .networkType(networkType)
                        .subnetID(subnetID));
        LOGGER.info("start to create network {}/{}", NAMESPACE, name);
        networkApi.create(network).throwsApiException();
        LOGGER.info("network created");
    }
    private static void waitNamespaceActive(CoreV1Api api) throws ApiException, InterruptedException {
        for (int i = 0; i < 5; i++) {
            V1Namespace ns = api.readNamespace(NAMESPACE, null, null, null);
            if (ns.getStatus() != null && NAMESPACE_ACTIVE.equals(ns.getStatus().getPhase())) {
                return;
            }
            Thread.sleep(WAIT_ACTIVE_MILLIS);
        }
        throw new IllegalStateException("namespace not active");
    }
    private static void waitNetworkActive(GenericKubernetesApi<Network, NetworkList> networkApi) throws ApiException, InterruptedException {
        for (int i = 0; i < 5; i++) {
            Network network = networkApi.get(NAMESPACE, NETWORK).throwsApiException().getObject();
            if (network.getStatus() != null && NETWORK_ACTIVE.equals(network.getStatus().getState())) {
                return;
            }
            Thread.sleep(WAIT_ACTIVE_MILLIS);
        }
        throw new IllegalStateException("network not active");
    }
    private static void createDeployment(AppsV1Api api) throws ApiException {
        String app = APP;
        String cpu = "500m";
        String memory = "1024Mi";
        String containerName = "container-0";
        String image = "library/nginx:stable-alpine-perl";
        Map<String, Quantity> limits = new HashMap<>();
        limits.put("cpu", Quantity.fromString(cpu));
        limits.put("memory", Quantity.fromString(memory));
        V1Container container = new V1Container()
                .name(containerName)
                .image(image)
                .resources(new V1ResourceRequirements().limits(limits).requests(limits));
        Map<String, String> labels = new HashMap<>();
        labels.put("app", app);
        V1PodTemplateSpec podTemplateSpec = new V1PodTemplateSpec()
                .spec(new V1PodSpec()
                        .priority(0)
                        .imagePullSecrets(Collections.singletonList(new V1LocalObjectReference().name("imagepull-secret")))
                        .containers(Collections.singletonList(container)))
                .metadata(new V1ObjectMeta().labels(labels));
        V1Deployment deployment = new V1Deployment()
                .metadata(new V1ObjectMeta().name(app))
                .spec(new V1DeploymentSpec()
                        .replicas(2)
                        .selector(new V1LabelSelector().matchLabels(labels))
                        .template(podTemplateSpec));
        LOGGER.info("start to create deployment {}/{}", NAMESPACE, APP);
        api.createNamespacedDeployment(NAMESPACE, deployment, null, null, null);
        LOGGER.info("deployment created");
    }
    private static void getDeployment(AppsV1Api api) throws ApiException {
        V1Deployment deployment = api.readNamespacedDeployment(APP, NAMESPACE, null, null, null);
        LOGGER.info("deployment metadata: {}", deployment.getMetadata());
    }
    private static void deleteDeployment(AppsV1Api api) throws ApiException {
        LOGGER.info("start to delete deployment");
        api.deleteNamespacedDeployment(APP, NAMESPACE, null, null, null, null, null, null);
        LOGGER.info("deployment deleted");
    }
    private static void deleteNamespace(CoreV1Api api) throws ApiException {
        LOGGER.info("start to delete namespace: {}", NAMESPACE);
        api.deleteNamespace(NAMESPACE, null, null, null, null, null, null);
        LOGGER.info("namespace deleted");
    }
}
```
#### FAQ
**问：以上示例是否适用于其他版本的kubernetes-client/java？**
**答：** 由于以上示例使用了GenericKubernetesClient（参考[Code-Examples](https://github.com/kubernetes-client/java/wiki/3.-Code-Examples)，需要9.0.0+以上版本），所以不适用9.0.0以下版本SDK。
另外由于不同版本SDK之间存在一定差别，上述示例代码需要做一些细微调整才能适用于不同版本SDK，请自行调试。
