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

feat(provider/kubernetes): v2 support labels on delete & scale #2190

Merged
merged 1 commit into from
Nov 28, 2017
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
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,40 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.netflix.spinnaker.clouddriver.kubernetes.description.KubernetesAtomicOperationDescription;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.KubernetesCoordinates;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesSelectorList;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.Pair;

import java.util.List;
import java.util.stream.Collectors;

@EqualsAndHashCode(callSuper = true)
@Data
public class KubernetesManifestOperationDescription extends KubernetesAtomicOperationDescription {
private String name;
private String location;
private List<String> kinds;
private KubernetesSelectorList labelSelectors = new KubernetesSelectorList();

@JsonIgnore
public boolean isDynamic() {
return StringUtils.isEmpty(name);
}

public List<KubernetesCoordinates> getAllCoordinates() {
return kinds.stream()
.map(k -> KubernetesCoordinates.builder()
.namespace(location)
.kind(KubernetesKind.fromString(k))
.build())
.collect(Collectors.toList());
}

@JsonIgnore
public KubernetesCoordinates getCoordinates() {
@Deprecated
public KubernetesCoordinates getPointCoordinates() {
Pair<KubernetesKind, String> parsedName = KubernetesManifest.fromFullResourceName(name);

return KubernetesCoordinates.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@

import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesKind;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.op.job.KubectlJobExecutor;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesSelectorList;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesV2Credentials;
import io.kubernetes.client.models.V1DeleteOptions;

public interface CanDelete {
KubernetesKind kind();

default void delete(KubernetesV2Credentials credentials, String namespace, String name, V1DeleteOptions options) {
credentials.delete(kind(), namespace, name, options);
default void delete(KubernetesV2Credentials credentials, String namespace, String name, KubernetesSelectorList labelSelectors, V1DeleteOptions options) {
credentials.delete(kind(), namespace, name, labelSelectors, options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
package com.netflix.spinnaker.clouddriver.kubernetes.v2.op.deployer;

import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesKind;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesSelectorList;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesV2Credentials;
import com.netflix.spinnaker.clouddriver.model.ServerGroup.Capacity;

public interface CanResize {
KubernetesKind kind();

default void resize(KubernetesV2Credentials credentials, String namespace, String name, Capacity capacity) {
credentials.scale(kind(), namespace, name, capacity.getDesired());
credentials.scale(kind(), namespace, name, new KubernetesSelectorList(), capacity.getDesired());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
package com.netflix.spinnaker.clouddriver.kubernetes.v2.op.deployer;

import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesKind;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesSelectorList;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesV2Credentials;

public interface CanScale {
KubernetesKind kind();

default void scale(KubernetesV2Credentials credentials, String namespace, String name, int replicas) {
credentials.scale(kind(), namespace, name, replicas);
default void scale(KubernetesV2Credentials credentials, String namespace, String name, KubernetesSelectorList labelSelectors, int replicas) {
credentials.scale(kind(), namespace, name, labelSelectors, replicas);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesKind;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesManifest;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesManifestList;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesSelectorList;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesV2Credentials;
import io.kubernetes.client.models.V1DeleteOptions;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -83,12 +84,12 @@ public String logs(KubernetesV2Credentials credentials, String namespace, String
return status.getStdOut();
}

public Void delete(KubernetesV2Credentials credentials, KubernetesKind kind, String namespace, String name, V1DeleteOptions deleteOptions) {
public Void delete(KubernetesV2Credentials credentials, KubernetesKind kind, String namespace, String name, KubernetesSelectorList labelSelectors, V1DeleteOptions deleteOptions) {
List<String> command = kubectlNamespacedAuthPrefix(credentials, namespace);

command.add("delete");
command.add(kind.toString());
command.add(name);

command = kubectlLookupInfo(command, kind, name, labelSelectors);

// spinnaker generally accepts deletes of resources that don't exist
command.add("--ignore-not-found=true");
Expand Down Expand Up @@ -118,11 +119,11 @@ public Void delete(KubernetesV2Credentials credentials, KubernetesKind kind, Str
return null;
}

public Void scale(KubernetesV2Credentials credentials, KubernetesKind kind, String namespace, String name, int replicas) {
public Void scale(KubernetesV2Credentials credentials, KubernetesKind kind, String namespace, String name, KubernetesSelectorList labelSelectors, int replicas) {
List<String> command = kubectlNamespacedAuthPrefix(credentials, namespace);

command.add("scale");
command.add(kind.toString() + "/" + name);
command = kubectlLookupInfo(command, kind, name, labelSelectors);
command.add("--replicas=" + replicas);

String jobId = jobExecutor.startJob(new JobRequest(command),
Expand Down Expand Up @@ -338,6 +339,21 @@ private List<String> kubectlAuthPrefix(KubernetesV2Credentials credentials) {
return command;
}

private List<String> kubectlLookupInfo(List<String> command, KubernetesKind kind, String name, KubernetesSelectorList labelSelectors) {
if (StringUtils.isNotEmpty(name)) {
command.add(kind + "/" + name);
} else {
command.add(kind.toString());
}

if (!labelSelectors.isEmpty()) {
command.add("-l");
command.add("'" + labelSelectors + "'");
}

return command;
}

private List<String> kubectlNamespacedAuthPrefix(KubernetesV2Credentials credentials, String namespace) {
List<String> command = kubectlAuthPrefix(credentials);
if (StringUtils.isEmpty(namespace)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesV2Credentials;
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperation;

import java.util.Collections;
import java.util.List;

public class KubernetesDeleteManifestOperation implements AtomicOperation<Void> {
Expand All @@ -49,23 +50,32 @@ private static Task getTask() {
@Override
public Void operate(List priorOutputs) {
getTask().updateStatus(OP_NAME, "Starting delete operation...");
KubernetesCoordinates coordinates = description.getCoordinates();
List<KubernetesCoordinates> coordinates;

getTask().updateStatus(OP_NAME, "Looking up resource properties...");
KubernetesResourceProperties properties = registry.get(coordinates.getKind());
KubernetesHandler deployer = properties.getHandler();

if (!(deployer instanceof CanDelete)) {
throw new IllegalArgumentException("Resource with " + coordinates + " does not support delete");
if (description.isDynamic()) {
coordinates = description.getAllCoordinates();
} else {
coordinates = Collections.singletonList(description.getPointCoordinates());
}

CanDelete canDelete = (CanDelete) deployer;
coordinates.forEach(c -> {
getTask().updateStatus(OP_NAME, "Looking up resource properties for " + c.getKind() + "...");
KubernetesResourceProperties properties = registry.get(c.getKind());
KubernetesHandler deployer = properties.getHandler();

if (!(deployer instanceof CanDelete)) {
throw new IllegalArgumentException("Resource with " + c + " does not support delete");
}

CanDelete canDelete = (CanDelete) deployer;

getTask().updateStatus(OP_NAME, "Calling delete operation...");
canDelete.delete(credentials,
coordinates.getNamespace(),
coordinates.getName(),
description.getOptions());
getTask().updateStatus(OP_NAME, "Calling delete operation...");
canDelete.delete(credentials,
c.getNamespace(),
c.getName(),
description.getLabelSelectors(),
description.getOptions());
});

return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private static Task getTask() {
@Override
public Void operate(List priorOutputs) {
getTask().updateStatus(OP_NAME, "Starting pause rollout operation...");
KubernetesCoordinates coordinates = description.getCoordinates();
KubernetesCoordinates coordinates = description.getPointCoordinates();

getTask().updateStatus(OP_NAME, "Looking up resource properties...");
KubernetesResourceProperties properties = registry.get(coordinates.getKind());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private static Task getTask() {
@Override
public Void operate(List priorOutputs) {
getTask().updateStatus(OP_NAME, "Starting resume rollout operation...");
KubernetesCoordinates coordinates = description.getCoordinates();
KubernetesCoordinates coordinates = description.getPointCoordinates();

getTask().updateStatus(OP_NAME, "Looking up resource properties...");
KubernetesResourceProperties properties = registry.get(coordinates.getKind());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesV2Credentials;
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperation;

import java.util.Collections;
import java.util.List;

public class KubernetesScaleManifestOperation implements AtomicOperation<Void> {
Expand All @@ -49,23 +50,32 @@ private static Task getTask() {
@Override
public Void operate(List priorOutputs) {
getTask().updateStatus(OP_NAME, "Starting scale operation...");
KubernetesCoordinates coordinates = description.getCoordinates();
List<KubernetesCoordinates> coordinates;

getTask().updateStatus(OP_NAME, "Looking up resource properties...");
KubernetesResourceProperties properties = registry.get(coordinates.getKind());
KubernetesHandler deployer = properties.getHandler();

if (!(deployer instanceof CanScale)) {
throw new IllegalArgumentException("Resource with " + coordinates + " does not support scale");
if (description.isDynamic()) {
coordinates = description.getAllCoordinates();
} else {
coordinates = Collections.singletonList(description.getPointCoordinates());
}

CanScale canScale = (CanScale) deployer;
coordinates.forEach(c -> {
getTask().updateStatus(OP_NAME, "Looking up resource properties...");
KubernetesResourceProperties properties = registry.get(c.getKind());
KubernetesHandler deployer = properties.getHandler();

if (!(deployer instanceof CanScale)) {
throw new IllegalArgumentException("Resource with " + c + " does not support scale");
}

CanScale canScale = (CanScale) deployer;

getTask().updateStatus(OP_NAME, "Calling scale operation...");
canScale.scale(credentials,
coordinates.getNamespace(),
coordinates.getName(),
description.getReplicas());
getTask().updateStatus(OP_NAME, "Calling scale operation...");
canScale.scale(credentials,
c.getNamespace(),
c.getName(),
description.getLabelSelectors(),
description.getReplicas());
});

return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private static Task getTask() {
@Override
public Void operate(List priorOutputs) {
getTask().updateStatus(OP_NAME, "Starting undo rollout operation...");
KubernetesCoordinates coordinates = description.getCoordinates();
KubernetesCoordinates coordinates = description.getPointCoordinates();

getTask().updateStatus(OP_NAME, "Looking up resource properties...");
KubernetesResourceProperties properties = registry.get(coordinates.getKind());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@

package com.netflix.spinnaker.clouddriver.kubernetes.v2.security;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;

import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.List;

@Data
public class KubernetesSelector {
private enum Kind {
ANY,
Expand All @@ -38,7 +42,12 @@ private enum Kind {
final private String key;
final private List<String> values;

private KubernetesSelector(@NotNull Kind kind, String key, List<String> values) {
@JsonCreator
private KubernetesSelector(
@JsonProperty("kind") @NotNull Kind kind,
@JsonProperty("key") String key,
@JsonProperty("values") List<String> values
) {
if (StringUtils.isEmpty(key) && kind != Kind.ANY) {
throw new IllegalArgumentException("Only an 'any' selector can have no key specified");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@

package com.netflix.spinnaker.clouddriver.kubernetes.v2.security;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Data
public class KubernetesSelectorList {
private final List<KubernetesSelector> selectors = new ArrayList<>();

Expand All @@ -36,6 +40,10 @@ public KubernetesSelectorList addSelector(KubernetesSelector selector) {
return this;
}

public boolean isEmpty() {
return selectors.isEmpty();
}

@Override
public String toString() {
return String.join(",", selectors.stream().map(KubernetesSelector::toString).collect(Collectors.toList()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,12 @@ public String logs(String namespace, String podName, String containerName) {
return runAndRecordMetrics("logs", KubernetesKind.POD, namespace, () -> jobExecutor.logs(this, namespace, podName, containerName));
}

public void scale(KubernetesKind kind, String namespace, String name, int replicas) {
runAndRecordMetrics("scale", kind, namespace, () -> jobExecutor.scale(this, kind, namespace, name, replicas));
public void scale(KubernetesKind kind, String namespace, String name, KubernetesSelectorList labelSelectors, int replicas) {
runAndRecordMetrics("scale", kind, namespace, () -> jobExecutor.scale(this, kind, namespace, name, labelSelectors, replicas));
}

public void delete(KubernetesKind kind, String namespace, String name, V1DeleteOptions options) {
runAndRecordMetrics("scale", kind, namespace, () -> jobExecutor.delete(this, kind, namespace, name, options));
public void delete(KubernetesKind kind, String namespace, String name, KubernetesSelectorList labelSelectors, V1DeleteOptions options) {
runAndRecordMetrics("scale", kind, namespace, () -> jobExecutor.delete(this, kind, namespace, name, labelSelectors, options));
}

public void deploy(KubernetesManifest manifest) {
Expand Down