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

Create OpenAPI Kubernetes client integration with OKE #1041

Merged
merged 5 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ managed-apache-http-core5 = "5.3.1"
micronaut-gradle-plugin = "4.4.4"
micronaut-groovy = "4.5.0"
micronaut-kotlin = "4.5.0"
micronaut-kubernetes = "6.2.1"
micronaut-micrometer = "5.9.3"
micronaut-reactor = "3.6.0"
micronaut-rxjava2 = "2.6.0"
Expand All @@ -41,6 +42,7 @@ micronaut-core = { module = 'io.micronaut:micronaut-core-bom', version.ref = 'mi
# micronaut boms
micronaut-groovy = { module = "io.micronaut.groovy:micronaut-groovy-bom", version.ref = "micronaut-groovy" }
micronaut-kotlin = { module = "io.micronaut.kotlin:micronaut-kotlin-bom", version.ref = "micronaut-kotlin" }
micronaut-kubernetes = { module = "io.micronaut.kubernetes:micronaut-kubernetes-bom", version.ref = "micronaut-kubernetes" }
micronaut-micrometer = { module = "io.micronaut.micrometer:micronaut-micrometer-bom", version.ref = "micronaut-micrometer" }
micronaut-reactor = { module = "io.micronaut.reactor:micronaut-reactor-bom", version.ref = "micronaut-reactor" }
micronaut-rxjava2 = { module = "io.micronaut.rxjava2:micronaut-rxjava2-bom", version.ref = "micronaut-rxjava2" }
Expand Down
28 changes: 28 additions & 0 deletions oraclecloud-oke-kubernetes-client/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
plugins {
id 'io.micronaut.build.internal.oraclecloud-module'
}

dependencies {
api mnKubernetes.micronaut.kubernetes.client.openapi.common
implementation mnValidation.validation
implementation projects.micronautOraclecloudBmcContainerengine

testImplementation mnKubernetes.micronaut.kubernetes.client.openapi
api projects.micronautOraclecloudCommon
testImplementation mn.micronaut.context
testAnnotationProcessor mn.micronaut.inject.java
testImplementation mn.micronaut.inject.java
testImplementation mn.micronaut.inject.groovy
testImplementation mn.micronaut.inject.groovy.test
testImplementation mn.micronaut.http.server.netty
}

tasks.withType(Test).configureEach {
useJUnitPlatform()
}

micronautBuild {
binaryCompatibility {
enabled = false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright 2017-2024 original authors
*
* 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
*
* https://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.micronaut.oraclecloud.oke.kubernetes.client;

import com.oracle.bmc.containerengine.ContainerEngineClient;
import com.oracle.bmc.containerengine.model.CreateClusterKubeconfigContentDetails;
import com.oracle.bmc.containerengine.model.CreateClusterKubeconfigContentDetails.Endpoint;
import com.oracle.bmc.containerengine.requests.CreateKubeconfigRequest;
import com.oracle.bmc.containerengine.responses.CreateKubeconfigResponse;
import io.micronaut.context.annotation.BootstrapContextCompatible;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.io.ResourceResolver;
import io.micronaut.kubernetes.client.openapi.config.AbstractKubeConfigLoader;
import io.micronaut.kubernetes.client.openapi.config.KubeConfig;
import io.micronaut.kubernetes.client.openapi.config.KubeConfigLoader;
import jakarta.inject.Singleton;
import java.io.IOException;
import java.io.InputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A class for loading a kubeconfig from OKE. It replaces the default kube config loader, which
* reads it from a file.
*
* @since 4.4.x
* @author Andriy Dmytruk
*/
@Singleton
@BootstrapContextCompatible
@Replaces(KubeConfigLoader.class)
@Requires(beans = OkeKubernetesClientConfig.class)
final class OkeKubeConfigLoader extends AbstractKubeConfigLoader {

private static final String TOKEN_VERSION = "2.0.0";

private static final Logger LOG = LoggerFactory.getLogger(OkeKubeConfigLoader.class);
private final ContainerEngineClient client;
private final OkeKubernetesClientConfig config;

/**
* Create Kubeconfig loader.
*
* @param containerEngineClient The client to use to get kubeconfig
* @param config The configuration
* @param resourceResolver The resource resolver.
*/
OkeKubeConfigLoader(
ContainerEngineClient containerEngineClient,
OkeKubernetesClientConfig config,
ResourceResolver resourceResolver
) {
super(resourceResolver);
this.client = containerEngineClient;
this.config = config;
}

@Override
protected @Nullable KubeConfig loadKubeConfig() {
return createCubeConfig(config.clusterId(), config.endpointType());
}

/**
* A method that uses the container engine client to create a kube config and write it to the
* file specified by the environment variable.
*
* @param okeClusterId The cluster ID
* @param endpointType The endpoint type
* @return The kubeconfig
*/
protected KubeConfig createCubeConfig(String okeClusterId, Endpoint endpointType) {
LOG.info("Creating remote kubeconfig for cluster id {}", okeClusterId);
CreateClusterKubeconfigContentDetails body = CreateClusterKubeconfigContentDetails.builder()
.tokenVersion(TOKEN_VERSION)
.endpoint(endpointType)
.build();
CreateKubeconfigRequest kubeConfigRequest = CreateKubeconfigRequest.builder()
.clusterId(okeClusterId)
.createClusterKubeconfigContentDetails(body)
.build();

CreateKubeconfigResponse response;
try {
response = client.createKubeconfig(kubeConfigRequest);
} catch (Exception e) {
LOG.error("Caught exception when creating kubeconfig for cluster: {}", okeClusterId, e);
throw new IllegalStateException("Unable to create KubeConfig", e);
}
LOG.info("Successfully received kubeconfig response for cluster id {}", okeClusterId);
try (InputStream kubeConfig = response.getInputStream()) {
return loadKubeConfigFromInputStream(kubeConfig);
} catch (IOException e) {
LOG.error("Caught exception when reading kubeconfig for cluster {}", okeClusterId, e);
throw new RuntimeException("Unable to create kubeClient", e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2017-2024 original authors
*
* 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
*
* https://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.micronaut.oraclecloud.oke.kubernetes.client;

import com.oracle.bmc.containerengine.model.CreateClusterKubeconfigContentDetails.Endpoint;
import io.micronaut.context.annotation.BootstrapContextCompatible;
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.bind.annotation.Bindable;
import io.micronaut.core.util.StringUtils;
import jakarta.validation.constraints.NotBlank;

/**
* Configuration for the OKE kubernetes client.
* If enabled, the client will retrieve a kubeconfig from the cluster and sign tokens
* with the OCI SDK authentication. This is not required on kubernetes nodes, as there
* kubeconfig should already be present.
*
* @param enabled Whether the client is enabled
* @param clusterId The OKE cluster ID
* @param endpointType Define which endpoint type to use. One of {@code PublicEndpoint},
* {@code PrivateEndpoint}, {@code VcnHostname} and {@code LegacyKubernetes}.
* Default is {@code PublicEndpoint}.
*
* @since 4.4.x
* @author Andriy Dmytruk
*/
@Requires(property = OkeKubernetesClientConfig.ENABLED, value = StringUtils.TRUE, defaultValue = StringUtils.TRUE)
@Requires(property = OkeKubernetesClientConfig.CLUSTER_ID)
@ConfigurationProperties(OkeKubernetesClientConfig.PREFIX)
@BootstrapContextCompatible
public record OkeKubernetesClientConfig(
andriy-dmytruk marked this conversation as resolved.
Show resolved Hide resolved
@Bindable(defaultValue = StringUtils.TRUE)
boolean enabled,
@NonNull @NotBlank
String clusterId,
andriy-dmytruk marked this conversation as resolved.
Show resolved Hide resolved
@Nullable @Bindable(defaultValue = "PublicEndpoint")
Endpoint endpointType
) {

public static final String PREFIX = "oci.oke.kubernetes.client";
public static final String ENABLED = PREFIX + ".enabled";
public static final String CLUSTER_ID = PREFIX + ".cluster-id";

}
Loading
Loading