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

fix #3334: adding basic support for server side apply #3937

Merged
merged 5 commits into from
Mar 12, 2022
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Remove `setIntVal`, `setStrVal`, `setKind` setters from `IntOrString` class to avoid invalid combinations
* Fix #3889 : remove piped stream for file download
* Fix #1285: removed references to manually calling registerCustomKind
* Fix #3334: adding basic support for server side apply. Use patch(PatchContext.of(PatchType.SERVER_SIDE_APPLY), service), or new PatchContext.Builder().withPatchType(PatchType.SERVER_SIDE_APPLY).withForce(true).build() to override conflicts.

#### Dependency Upgrade
* Fix #3788: Point CamelK Extension model to latest released version v1.8.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public class Config {
public static final String KUBERNETES_PROXY_USERNAME = "proxy.username";
public static final String KUBERNETES_PROXY_PASSWORD = "proxy.password";

public static final String KUBERNETES_USER_AGENT = "fabric8-kubernetes-client/" + Version.clientVersion();
public static final String KUBERNETES_USER_AGENT = "kubernetes.user.agent";

public static final String DEFAULT_MASTER_URL = "https://kubernetes.default.svc";
public static final Long DEFAULT_ROLLING_TIMEOUT = 15 * 60 * 1000L;
Expand Down Expand Up @@ -217,7 +217,7 @@ public class Config {
private String proxyUsername;
private String proxyPassword;
private String[] noProxy;
private String userAgent;
private String userAgent = "fabric8-kubernetes-client/" + Version.clientVersion();
private TlsVersion[] tlsVersions = new TlsVersion[] { TlsVersion.TLS_1_2 };

private Map<Integer, String> errorMessages = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ public interface Patchable<T> {
/**
* Update field(s) of a resource using a JSON patch.
*
* <br>It is the same as calling {@link #patch(PatchContext, Object)} with {@link PatchType#JSON} specified.
* <br>
* It is the same as calling {@link #patch(PatchContext, Object)} with {@link PatchType#JSON} specified.
*
* WARNING: This may overwrite concurrent changes (between when you obtained your item and the current state) in an unexpected way.
* Consider using edit instead.
* WARNING: This may overwrite concurrent changes (between when you obtained your item and the current state) in an unexpected
* way.
* Consider using edit instead or ensure you have called load or withItem to define the base of your patch
*
* @param item to be patched with patched values
* @return returns deserialized version of api server response
Expand All @@ -39,13 +41,14 @@ default T patch(T item) {
* Update field(s) of a resource using type specified in {@link PatchContext}(defaults to strategic merge if not specified).
*
* <ul>
* <li>{@link PatchType#JSON} - will create a JSON patch against the current item.
* WARNING: This may overwrite concurrent changes (between when you obtained your item and the current state) in an unexpected way.
* Consider using edit instead.
* <li>{@link PatchType#JSON} - will create a JSON patch against the current item. See the note in {@link #patch(Object)}
* about what is used for the base object.
* <li>{@link PatchType#JSON_MERGE} - will send the serialization of the item as a JSON MERGE patch.
* Set the resourceVersion to null to prevent optimistic locking.
* <li>{@link PatchType#STRATEGIC_MERGE} - will send the serialization of the item as a STRATEGIC MERGE patch.
* Set the resourceVersion to null to prevent optimistic locking.
* <li>{@link PatchType#SERVER_SIDE_APPLY} - will send the serialization of the item as a SERVER SIDE APPLY patch.
* You may explicitly set the {@link PatchContext#getFieldManager()} as well to override the default.
* </ul>
*
* @param item to be patched with patched values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
import java.util.List;

public class PatchContext {
// TODO this state overlaps with PatchOptions
private List<String> dryRun;
private String fieldManager;
private Boolean force;
private PatchType patchType;

private String fieldValidation;

public static PatchContext of(PatchType type) {
return new PatchContext.Builder().withPatchType(type).build();
}
Expand Down Expand Up @@ -59,6 +61,14 @@ public void setPatchType(PatchType patchType) {
this.patchType = patchType;
}

public String getFieldValidation() {
return fieldValidation;
}

public void setFieldValidation(String fieldValidation) {
this.fieldValidation = fieldValidation;
}

public static class Builder {
private final PatchContext patchContext;

Expand Down Expand Up @@ -86,6 +96,11 @@ public Builder withPatchType(PatchType patchType) {
return this;
}

public Builder withFieldValidation(String fieldValidation) {
this.patchContext.setFieldValidation(fieldValidation);
return this;
}

public PatchContext build() {
return this.patchContext;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
public enum PatchType {
JSON("application/json-patch+json"),
JSON_MERGE("application/merge-patch+json"),
STRATEGIC_MERGE("application/strategic-merge-patch+json");
STRATEGIC_MERGE("application/strategic-merge-patch+json"),
SERVER_SIDE_APPLY("application/apply-patch+yaml");

private final String contentType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ public class ConfigTest {
private static final String TEST_KUBECONFIG_EXEC_FILE = Utils.filePath(ConfigTest.class.getResource("/test-kubeconfig-exec"));
private static final String TEST_TOKEN_GENERATOR_FILE = Utils.filePath(ConfigTest.class.getResource("/token-generator"));

private static final String TEST_KUBECONFIG_EXEC_WIN_FILE = Utils.filePath(ConfigTest.class.getResource("/test-kubeconfig-exec-win"));
private static final String TEST_KUBECONFIG_EXEC_WIN_FILE = Utils
.filePath(ConfigTest.class.getResource("/test-kubeconfig-exec-win"));

private static final String TEST_KUBECONFIG_NO_CURRENT_CONTEXT_FILE = Utils.filePath(ConfigTest.class.getResource("/test-kubeconfig-nocurrentctxt.yml"));
private static final String TEST_KUBECONFIG_NO_CURRENT_CONTEXT_FILE = Utils
.filePath(ConfigTest.class.getResource("/test-kubeconfig-nocurrentctxt.yml"));

@BeforeEach
public void setUp() {
Expand Down Expand Up @@ -146,41 +148,40 @@ void testWithSystemProperties() {
@Test
void testWithBuilder() {
Config config = new ConfigBuilder()
.withMasterUrl("http://somehost:80")
.withApiVersion("v1")
.withNamespace("testns")
.withOauthToken("token")
.withUsername("user")
.withPassword("pass")
.withTrustCerts(true)
.withDisableHostnameVerification(true)
.withCaCertFile("/path/to/cert")
.withCaCertData("cacertdata")
.withClientCertFile("/path/to/clientcert")
.withClientCertData("clientcertdata")
.withClientKeyFile("/path/to/clientkey")
.withClientKeyData("clientkeydata")
.withClientKeyAlgo("algo")
.withClientKeyPassphrase("passphrase")
.withMaxConcurrentRequests(120)
.withMaxConcurrentRequestsPerHost(20)
.withWatchReconnectInterval(5000)
.withWatchReconnectLimit(5)
.withRequestTimeout(5000)
.withUploadConnectionTimeout(60000)
.withUploadRequestTimeout(600000)
.withHttpProxy("httpProxy")
.withTlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1)
.withTrustStoreFile("/path/to/truststore")
.withTrustStorePassphrase("truststorePassphrase")
.withKeyStoreFile("/path/to/keystore")
.withKeyStorePassphrase("keystorePassphrase")
.build();
.withMasterUrl("http://somehost:80")
.withApiVersion("v1")
.withNamespace("testns")
.withOauthToken("token")
.withUsername("user")
.withPassword("pass")
.withTrustCerts(true)
.withDisableHostnameVerification(true)
.withCaCertFile("/path/to/cert")
.withCaCertData("cacertdata")
.withClientCertFile("/path/to/clientcert")
.withClientCertData("clientcertdata")
.withClientKeyFile("/path/to/clientkey")
.withClientKeyData("clientkeydata")
.withClientKeyAlgo("algo")
.withClientKeyPassphrase("passphrase")
.withMaxConcurrentRequests(120)
.withMaxConcurrentRequestsPerHost(20)
.withWatchReconnectInterval(5000)
.withWatchReconnectLimit(5)
.withRequestTimeout(5000)
.withUploadConnectionTimeout(60000)
.withUploadRequestTimeout(600000)
.withHttpProxy("httpProxy")
.withTlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1)
.withTrustStoreFile("/path/to/truststore")
.withTrustStorePassphrase("truststorePassphrase")
.withKeyStoreFile("/path/to/keystore")
.withKeyStorePassphrase("keystorePassphrase")
.build();

assertConfig(config);
}


@Test
void testWithBuilderAndSystemProperties() {
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://tobeoverriden:80");
Expand Down Expand Up @@ -218,9 +219,9 @@ void testWithBuilderAndSystemProperties() {
System.setProperty(Config.KUBERNETES_UPLOAD_REQUEST_TIMEOUT_SYSTEM_PROPERTY, "600000");

Config config = new ConfigBuilder()
.withMasterUrl("http://somehost:80")
.withNamespace("testns")
.build();
.withMasterUrl("http://somehost:80")
.withNamespace("testns")
.build();

assertConfig(config);
}
Expand Down Expand Up @@ -307,8 +308,8 @@ void testWithKubeConfigAndSytemPropertiesAndBuilder() {
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://somehost:80");

Config config = new ConfigBuilder()
.withNamespace("testns2")
.build();
.withNamespace("testns2")
.build();

assertNotNull(config);
assertEquals("http://somehost:80/", config.getMasterUrl());
Expand Down Expand Up @@ -387,8 +388,8 @@ void testWithNamespacePathAndSytemPropertiesAndBuilder() {
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "tobeoverriden");

Config config = new ConfigBuilder()
.withNamespace("testns2")
.build();
.withNamespace("testns2")
.build();

assertNotNull(config);
assertEquals("http://somehost:80/", config.getMasterUrl());
Expand All @@ -397,16 +398,16 @@ void testWithNamespacePathAndSytemPropertiesAndBuilder() {

@Test
void testWithCustomHeader() {
Map<String,String> customHeaders = new HashMap<>();
customHeaders.put("user-id","test-user");
customHeaders.put("cluster-id","test-cluster");
Map<String, String> customHeaders = new HashMap<>();
customHeaders.put("user-id", "test-user");
customHeaders.put("cluster-id", "test-cluster");
Config config = new ConfigBuilder()
.withCustomHeaders(customHeaders)
.build();
.withCustomHeaders(customHeaders)
.build();

assertNotNull(config);
assertNotNull(config.getCustomHeaders());
assertEquals(2,config.getCustomHeaders().size());
assertEquals(2, config.getCustomHeaders().size());
}

@Test
Expand All @@ -419,12 +420,12 @@ void shouldSetImpersonateUsernameAndGroupFromSystemProperty() {
extras.put("c", Collections.singletonList("d"));

final Config config = new ConfigBuilder()
.withImpersonateUsername("a")
.withImpersonateExtras(extras)
.build();
.withImpersonateUsername("a")
.withImpersonateExtras(extras)
.build();

assertEquals("a", config.getImpersonateUsername());
assertArrayEquals(new String[]{"group"}, config.getImpersonateGroups());
assertArrayEquals(new String[] { "group" }, config.getImpersonateGroups());
assertEquals(Collections.singletonList("d"), config.getImpersonateExtras().get("c"));

}
Expand All @@ -449,8 +450,8 @@ void honorClientAuthenticatorCommands() throws Exception {
void shouldBeUsedTokenSuppliedByProvider() {

Config config = new ConfigBuilder().withOauthToken("oauthToken")
.withOauthTokenProvider(() -> "PROVIDER_TOKEN")
.build();
.withOauthTokenProvider(() -> "PROVIDER_TOKEN")
.build();

assertEquals("PROVIDER_TOKEN", config.getOauthToken());
}
Expand All @@ -463,13 +464,14 @@ void shouldHonorDefaultWebsocketPingInterval() {
}

@Test
void testKubeConfigWithAuthConfigProvider() throws URISyntaxException {
void testKubeConfigWithAuthConfigProvider() throws URISyntaxException {
System.setProperty("kubeconfig", new File(getClass().getResource("/test-kubeconfig").toURI()).getAbsolutePath());
Config config = Config.autoConfigure("production/172-28-128-4:8443/mmosley");

assertEquals("https://172.28.128.4:8443/", config.getMasterUrl());
assertEquals("eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw",
config.getOauthToken());
assertEquals(
"eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw",
config.getOauthToken());
}

@Test
Expand Down Expand Up @@ -507,6 +509,7 @@ void testEmptyConfig() {
assertFalse(emptyConfig.isHttp2Disable());
assertEquals(1, emptyConfig.getTlsVersions().length);
assertTrue(emptyConfig.getErrorMessages().isEmpty());
assertNotNull(emptyConfig.getUserAgent());
}

private void assertConfig(Config config) {
Expand Down Expand Up @@ -536,7 +539,7 @@ private void assertConfig(Config config) {
assertEquals(60000, config.getRequestConfig().getUploadConnectionTimeout());
assertEquals(600000, config.getRequestConfig().getUploadRequestTimeout());

assertArrayEquals(new TlsVersion[]{TlsVersion.TLS_1_2, TlsVersion.TLS_1_1}, config.getTlsVersions());
assertArrayEquals(new TlsVersion[] { TlsVersion.TLS_1_2, TlsVersion.TLS_1_1 }, config.getTlsVersions());

assertEquals("/path/to/truststore", config.getTrustStoreFile());
assertEquals("truststorePassphrase", config.getTrustStorePassphrase());
Expand All @@ -555,13 +558,14 @@ void testGetAuthenticatorCommandFromExecConfig() throws IOException {
boolean isNewFileCreated = commandFile.createNewFile();
String systemPathValue = getTestPathValue(commandFolder);
ExecConfig execConfig = new ExecConfigBuilder()
.withApiVersion("client.authentication.k8s.io/v1alpha1")
.addToArgs("--region", "us-west2", "eks", "get-token", "--cluster-name", "api-eks.example.com")
.withCommand("aws")
.build();
.withApiVersion("client.authentication.k8s.io/v1alpha1")
.addToArgs("--region", "us-west2", "eks", "get-token", "--cluster-name", "api-eks.example.com")
.withCommand("aws")
.build();

// When
List<String> processBuilderArgs = Config.getAuthenticatorCommandFromExecConfig(execConfig, new File("~/.kube/config"), systemPathValue);
List<String> processBuilderArgs = Config.getAuthenticatorCommandFromExecConfig(execConfig, new File("~/.kube/config"),
systemPathValue);

// Then
assertTrue(isNewFileCreated);
Expand All @@ -587,12 +591,12 @@ private void assertPlatformPrefixes(List<String> processBuilderArgs) {
private String getTestPathValue(File commandFolder) {
if (Utils.isWindowsOperatingSystem()) {
return "C:\\Program Files\\Java\\jdk14.0_23\\bin" + File.pathSeparator +
commandFolder.getAbsolutePath() + File.pathSeparator +
"C:\\Program Files\\Apache Software Foundation\\apache-maven-3.3.1";
commandFolder.getAbsolutePath() + File.pathSeparator +
"C:\\Program Files\\Apache Software Foundation\\apache-maven-3.3.1";
} else {
return "/usr/java/jdk-14.0.1/bin" + File.pathSeparator +
commandFolder.getAbsolutePath() + File.pathSeparator +
"/opt/apache-maven/bin";
commandFolder.getAbsolutePath() + File.pathSeparator +
"/opt/apache-maven/bin";
}
}
}
Loading