Skip to content

Commit

Permalink
fix(manifest-replicas): ReplicaSet Source-Capacity (#5874)
Browse files Browse the repository at this point in the history
  • Loading branch information
nhtzr authored Feb 23, 2023
1 parent 07b1d87 commit a4557e9
Showing 5 changed files with 422 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1588,4 +1588,192 @@ public void shouldGetDeployedCrdsCredentials() throws IOException, InterruptedEx
private static String generateManifestName(String myconfig) {
return myconfig + Long.toHexString(UUID.randomUUID().getLeastSignificantBits());
}

@DisplayName(
".\n===\n"
+ "Given a deployed manifest with an special annotation to avoid being versioned\n"
+ " And another annotation to avoid updating the replicas amount"
+ "When sending an updated manifest\n"
+ " With a a new env var\n"
+ " And a different replica size\n"
+ "The manifest is deployed with the new env var and the old replicas size\n===")
@Test
public void shouldUseSourceCapacityNonVersioned() throws IOException, InterruptedException {
// ------------------------- given --------------------------
int originalReplicasSize = 1;
int secondReplicasSize = 5;
ImmutableMap<String, String> annotations =
ImmutableMap.of(
"strategy.spinnaker.io/versioned",
"false" // non-versioned
,
"strategy.spinnaker.io/use-source-capacity",
"true" // do not update replicas
);
String appName = "unversionedsourcepacaity-deployment";
System.out.println("> Using namespace: " + account1Ns + ", appName: " + appName);
List<Map<String, Object>> manifest =
KubeTestUtils.loadYaml("classpath:manifests/deployment.yml")
.withValue("metadata.namespace", account1Ns)
.withValue("metadata.name", appName)
.withValue("spec.selector.matchLabels.app", appName)
.withValue("spec.template.metadata.labels.app", appName)
.withValue("spec.replicas", originalReplicasSize)
.withValue("metadata.annotations", annotations)
.asList();
List<Map<String, Object>> body =
KubeTestUtils.loadJson("classpath:requests/deploy_manifest.json")
.withValue("deployManifest.account", ACCOUNT1_NAME)
.withValue("deployManifest.moniker.app", appName)
.withValue("deployManifest.manifests", manifest)
.asList();
System.out.println("> Using namespace: " + account1Ns + ", appName: " + appName);
KubeTestUtils.deployAndWaitStable(baseUrl(), body, account1Ns, "deployment " + appName);

// ------------------------- when --------------------------
List<Map<String, Object>> secondManifest =
KubeTestUtils.loadYaml("classpath:manifests/deployment.yml")
.withValue("metadata.namespace", account1Ns)
.withValue("metadata.name", appName)
.withValue("spec.selector.matchLabels.app", appName)
.withValue("spec.template.metadata.labels.app", appName)
.withValue("spec.replicas", secondReplicasSize)
.withValue(
"spec.template.spec.containers[0].env",
Collections.singletonList(
ImmutableMap.of(
"name", "test",
"value", "test")))
.withValue("metadata.annotations", annotations)
.asList();
List<Map<String, Object>> secondBody =
KubeTestUtils.loadJson("classpath:requests/deploy_manifest.json")
.withValue("deployManifest.account", ACCOUNT1_NAME)
.withValue("deployManifest.moniker.app", appName)
.withValue("deployManifest.manifests", secondManifest)
.asList();
System.out.println("> Using namespace: " + account1Ns + ", appName: " + appName);
KubeTestUtils.deployAndWaitStable(baseUrl(), secondBody, account1Ns, "deployment " + appName);

// ------------------------- then --------------------------
String currentReplicas =
kubeCluster.execKubectl(
"-n " + account1Ns + " get deployment " + appName + " -o=jsonpath='{.spec.replicas}'");
assertEquals(
String.valueOf(originalReplicasSize),
currentReplicas,
"Expected "
+ originalReplicasSize
+ " replica for "
+ appName
+ " deployment. Pods:\n"
+ currentReplicas);

String envVarValue =
kubeCluster.execKubectl(
"-n "
+ account1Ns
+ " get deployment "
+ appName
+ " -o=jsonpath='{.spec.template.spec.containers[0].env[0].value}'");
assertEquals("test", envVarValue, "Expected update env var for " + appName + " deployment.\n");
}

@DisplayName(
".\n===\n"
+ "Given a replicaset manifest with an special annotation for versioning\n"
+ " And another annotation to avoid updating the replicas amount"
+ "When sending an updated manifest\n"
+ " With a a new env var\n"
+ " And a different replica size\n"
+ "The manifest is deployed with the new env var and the replicas value from the previous version\n===")
@Test
public void shouldUseSourceCapacityVersioned() throws IOException, InterruptedException {
// ------------------------- given --------------------------
int originalReplicasSize = 1;
int secondReplicasSize = 5;
ImmutableMap<String, String> annotations =
ImmutableMap.of(
"strategy.spinnaker.io/versioned",
"true" // versioned
,
"strategy.spinnaker.io/use-source-capacity",
"true" // do not update replicas
);
String appName = "unversionedsourcepacaity-replicaset";
System.out.println("> Using namespace: " + account1Ns + ", appName: " + appName);
List<Map<String, Object>> manifest =
KubeTestUtils.loadYaml("classpath:manifests/replicaset.yml")
.withValue("metadata.namespace", account1Ns)
.withValue("metadata.name", appName)
.withValue("spec.selector.matchLabels.app", appName)
.withValue("spec.template.metadata.labels.app", appName)
.withValue("spec.replicas", originalReplicasSize)
.withValue("metadata.annotations", annotations)
.asList();
List<Map<String, Object>> body =
KubeTestUtils.loadJson("classpath:requests/deploy_manifest.json")
.withValue("deployManifest.account", ACCOUNT1_NAME)
.withValue("deployManifest.moniker.app", appName)
.withValue("deployManifest.manifests", manifest)
.asList();
System.out.println("> Using namespace: " + account1Ns + ", appName: " + appName);
KubeTestUtils.deployAndWaitStable(
baseUrl(), body, account1Ns, "replicaSet " + appName + "-v000");

// ------------------------- when --------------------------
List<Map<String, Object>> secondManifest =
KubeTestUtils.loadYaml("classpath:manifests/replicaset.yml")
.withValue("metadata.namespace", account1Ns)
.withValue("metadata.name", appName)
.withValue("spec.selector.matchLabels.app", appName)
.withValue("spec.template.metadata.labels.app", appName)
.withValue("spec.replicas", secondReplicasSize)
.withValue(
"spec.template.spec.containers[0].env",
Collections.singletonList(
ImmutableMap.of(
"name", "test",
"value", "test")))
.withValue("metadata.annotations", annotations)
.asList();
List<Map<String, Object>> secondBody =
KubeTestUtils.loadJson("classpath:requests/deploy_manifest.json")
.withValue("deployManifest.account", ACCOUNT1_NAME)
.withValue("deployManifest.moniker.app", appName)
.withValue("deployManifest.manifests", secondManifest)
.asList();
System.out.println("> Using namespace: " + account1Ns + ", appName: " + appName);
KubeTestUtils.deployAndWaitStable(
baseUrl(), secondBody, account1Ns, "replicaSet " + appName + "-v001");

// ------------------------- then --------------------------
String currentReplicas =
kubeCluster.execKubectl(
"-n "
+ account1Ns
+ " get replicaSet "
+ appName
+ "-v001"
+ " -o=jsonpath='{.spec.replicas}'");
assertEquals(
String.valueOf(originalReplicasSize),
currentReplicas,
"Expected "
+ originalReplicasSize
+ " replica for "
+ appName
+ " replicaset. Pods:\n"
+ currentReplicas);

String envVarValue =
kubeCluster.execKubectl(
"-n "
+ account1Ns
+ " get replicaSet "
+ appName
+ "-v001"
+ " -o=jsonpath='{.spec.template.spec.containers[0].env[0].value}'");
assertEquals("test", envVarValue, "Expected update env var for " + appName + " replicaset.\n");
}
}
Original file line number Diff line number Diff line change
@@ -66,6 +66,14 @@ public OptionalInt getVersion(KubernetesManifest manifest, KubernetesCredentials
}
}

public OptionalInt getLatestVersion(
KubernetesManifest manifest, KubernetesCredentials credentials) {
ImmutableList<Artifact> priorVersions =
artifactProvider.getArtifacts(
manifest.getKind(), manifest.getName(), manifest.getNamespace(), credentials);
return findLatestVersion(priorVersions);
}

private static OptionalInt parseVersion(String versionString) {
if (!versionString.startsWith("v")) {
return OptionalInt.empty();
@@ -78,8 +86,15 @@ private static OptionalInt parseVersion(String versionString) {
}

private int findGreatestUnusedVersion(List<Artifact> priorVersions) {
int maxTaken = extractVersions(priorVersions.stream()).max().orElse(-1);
return maxTaken + 1;
OptionalInt latestVersion = findLatestVersion(priorVersions);
if (latestVersion.isPresent()) {
return latestVersion.getAsInt() + 1;
}
return 0;
}

private OptionalInt findLatestVersion(List<Artifact> priorVersions) {
return extractVersions(priorVersions.stream()).max();
}

private OptionalInt findMatchingVersion(
Original file line number Diff line number Diff line change
@@ -19,20 +19,33 @@

import com.netflix.spinnaker.clouddriver.kubernetes.description.KubernetesCoordinates;
import com.netflix.spinnaker.clouddriver.kubernetes.security.KubernetesCredentials;
import java.util.OptionalInt;

public class KubernetesSourceCapacity {
public static Integer getSourceCapacity(
KubernetesManifest manifest, KubernetesCredentials credentials) {
KubernetesManifest manifest, KubernetesCredentials credentials, OptionalInt currentVersion) {
String name = currentManifestName(manifest, currentVersion);
KubernetesManifest currentManifest =
credentials.get(
KubernetesCoordinates.builder()
.kind(manifest.getKind())
.namespace(manifest.getNamespace())
.name(manifest.getName())
.name(name)
.build());
if (currentManifest != null) {
return currentManifest.getReplicas();
}
return null;
}

private static String currentManifestName(
KubernetesManifest manifest, OptionalInt currentVersion) {
if (currentVersion.isEmpty()) {
return manifest.getName();
}

int version = currentVersion.getAsInt();
String versionString = String.format("v%03d", version);
return String.join("-", manifest.getName(), versionString);
}
}
Original file line number Diff line number Diff line change
@@ -117,8 +117,10 @@ public OperationResult operate(List<OperationResult> _unused) {

KubernetesHandler deployer = properties.getHandler();
if (strategy.isUseSourceCapacity() && deployer instanceof CanScale) {
OptionalInt latestVersion = latestVersion(manifest, version);
Integer replicas =
KubernetesSourceCapacity.getSourceCapacity(manifest, credentials);
KubernetesSourceCapacity.getSourceCapacity(
manifest, credentials, latestVersion);
if (replicas != null) {
manifest.setReplicas(replicas);
}
@@ -178,6 +180,15 @@ public OperationResult operate(List<OperationResult> _unused) {
return result;
}

@NotNull
private OptionalInt latestVersion(KubernetesManifest manifest, OptionalInt version) {
if (version.isEmpty()) {
return OptionalInt.empty();
}
OptionalInt latestVersion = resourceVersioner.getLatestVersion(manifest, credentials);
return latestVersion;
}

@NotNull
private List<KubernetesManifest> getManifestsFromDescription() {
List<KubernetesManifest> inputManifests = description.getManifests();
Loading

0 comments on commit a4557e9

Please sign in to comment.