diff --git a/chaosengine-experiments/chaosengine-kubernetes/pom.xml b/chaosengine-experiments/chaosengine-kubernetes/pom.xml index 6a035b124..589051e38 100644 --- a/chaosengine-experiments/chaosengine-kubernetes/pom.xml +++ b/chaosengine-experiments/chaosengine-kubernetes/pom.xml @@ -15,7 +15,7 @@ io.kubernetes client-java - 9.0.2 + 10.0.0 compile diff --git a/chaosengine-experiments/chaosengine-kubernetes/src/main/java/com/thales/chaos/container/impl/KubernetesPodContainer.java b/chaosengine-experiments/chaosengine-kubernetes/src/main/java/com/thales/chaos/container/impl/KubernetesPodContainer.java index ee9f08901..9bc900036 100644 --- a/chaosengine-experiments/chaosengine-kubernetes/src/main/java/com/thales/chaos/container/impl/KubernetesPodContainer.java +++ b/chaosengine-experiments/chaosengine-kubernetes/src/main/java/com/thales/chaos/container/impl/KubernetesPodContainer.java @@ -43,7 +43,7 @@ public class KubernetesPodContainer extends Container { private String podName; @Identifier(order = 2) private String namespace; - private Map labels = new HashMap<>(); + private final Map labels = new HashMap<>(); private boolean isBackedByController = false; private KubernetesPlatform kubernetesPlatform; @Identifier(order = 3) @@ -52,7 +52,8 @@ public class KubernetesPodContainer extends Container { private String ownerName; private Collection subcontainers = new HashSet<>(); private String targetedSubcontainer; - private Callable replicaSetRecovered = () -> kubernetesPlatform.replicaSetRecovered(this); + private final Callable replicaSetRecovered = () -> kubernetesPlatform.replicaSetRecovered(this); + private String parentNode; private KubernetesPodContainer () { super(); @@ -82,6 +83,10 @@ public String getOwnerName () { return ownerName; } + public String getParentNode () { + return parentNode; + } + @Override public void startExperiment (Experiment experiment) { this.targetedSubcontainer = null; @@ -153,6 +158,7 @@ public static final class KubernetesPodContainerBuilder { private String ownerKind; private String ownerName; private Collection subcontainers; + private String parentNode; private KubernetesPodContainerBuilder () { } @@ -211,6 +217,11 @@ public KubernetesPodContainerBuilder withUUID (String uuid) { return this; } + public KubernetesPodContainerBuilder withParentNode (String parentNode) { + this.parentNode = parentNode; + return this; + } + public KubernetesPodContainer build () { KubernetesPodContainer kubernetesPodContainer = new KubernetesPodContainer(); kubernetesPodContainer.uuid = this.uuid; @@ -223,6 +234,7 @@ public KubernetesPodContainer build () { kubernetesPodContainer.ownerKind = ControllerKind.mapFromString(this.ownerKind); kubernetesPodContainer.ownerName = ownerName; kubernetesPodContainer.subcontainers = this.subcontainers; + kubernetesPodContainer.parentNode = this.parentNode; try { kubernetesPodContainer.setMappedDiagnosticContext(); kubernetesPodContainer.log.info("Created new Kubernetes Pod Container object"); diff --git a/chaosengine-experiments/chaosengine-kubernetes/src/main/java/com/thales/chaos/platform/impl/KubernetesPlatform.java b/chaosengine-experiments/chaosengine-kubernetes/src/main/java/com/thales/chaos/platform/impl/KubernetesPlatform.java index 636846f28..10fa7b538 100644 --- a/chaosengine-experiments/chaosengine-kubernetes/src/main/java/com/thales/chaos/platform/impl/KubernetesPlatform.java +++ b/chaosengine-experiments/chaosengine-kubernetes/src/main/java/com/thales/chaos/platform/impl/KubernetesPlatform.java @@ -49,6 +49,7 @@ import static com.thales.chaos.constants.DataDogConstants.DATADOG_CONTAINER_KEY; import static com.thales.chaos.exception.enums.KubernetesChaosErrorCode.K8S_API_ERROR; +import static java.util.function.Predicate.not; import static net.logstash.logback.argument.StructuredArguments.v; @Component @@ -57,31 +58,35 @@ public class KubernetesPlatform extends Platform implements ShellBasedExperiment { @Autowired private ContainerManager containerManager; - @Autowired - private ApiClient apiClient; - private String namespace = "default"; + private final ApiClient apiClient; + static final String DEFAULT_NAMESPACE = "default"; + static final String UNKNOWN_PARENT_NODE = "UNKNOWN"; + private Collection namespaces = List.of(DEFAULT_NAMESPACE); - @Autowired - KubernetesPlatform (ApiClient apiClient) { - this.apiClient = apiClient; - log.info("Kubernetes Platform created"); + public Collection getNamespaces () { + return namespaces; } - public String getNamespace () { - return namespace; + public void setNamespaces (String namespaces) { + this.namespaces = Optional.of(Arrays.stream(namespaces.split(",")) + .filter(Objects::nonNull) + .filter(not(String::isEmpty)) + .collect(Collectors.toList())) + .filter(l -> !l.isEmpty()) + .orElse(List.of(DEFAULT_NAMESPACE)); } - public void setNamespace (String namespace) { - this.namespace = namespace; + @Autowired + KubernetesPlatform (ApiClient apiClient) { + this.apiClient = apiClient; + log.info("Kubernetes Platform created"); } public ContainerHealth checkHealth (KubernetesPodContainer kubernetesPodContainer) { try { - Optional podExists = podExists(kubernetesPodContainer); - if (podExists.isEmpty()) { - return ContainerHealth.RUNNING_EXPERIMENT; - } else if (!podExists.get()) { + boolean podExists = podExists(kubernetesPodContainer); + if (!podExists) { return ContainerHealth.DOES_NOT_EXIST; } V1Pod result = getCoreV1Api().readNamespacedPodStatus(kubernetesPodContainer.getPodName(), @@ -103,22 +108,19 @@ public ContainerHealth checkHealth (KubernetesPodContainer kubernetesPodContaine return ContainerHealth.RUNNING_EXPERIMENT; } - Optional podExists (KubernetesPodContainer kubernetesPodContainer) { + boolean podExists (KubernetesPodContainer kubernetesPodContainer) { String podUuid = kubernetesPodContainer.getUuid(); - if (podUuid == null) return Optional.empty(); - try { - Boolean podExists = listAllPodsInNamespace().getItems() - .stream() - .map(V1Pod::getMetadata) - .filter(Objects::nonNull) - .map(V1ObjectMeta::getUid) - .anyMatch(podUuid::equals); - log.debug("Kubernetes POD {} exists = {}", kubernetesPodContainer.getPodName(), podExists); - return Optional.of(podExists); - } catch (ApiException e) { - log.debug("Exception when checking container existence", e); - return Optional.empty(); + if (podUuid == null) { + return false; } + boolean podExists = listAllPodsInNamespace(kubernetesPodContainer.getNamespace()).getItems() + .stream() + .map(V1Pod::getMetadata) + .filter(Objects::nonNull) + .map(V1ObjectMeta::getUid) + .anyMatch(podUuid::equals); + log.debug("Kubernetes POD {} exists = {}", kubernetesPodContainer.getPodName(), podExists); + return podExists; } @JsonIgnore @@ -126,8 +128,30 @@ CoreV1Api getCoreV1Api () { return new CoreV1Api(apiClient); } - private V1PodList listAllPodsInNamespace () throws ApiException { - return getCoreV1Api().listNamespacedPod(namespace, "true", false, "", "", "", 0, "", 0, false); + private V1PodList listAllPodsInNamespace (String namespace) { + try { + return getCoreV1Api().listNamespacedPod(namespace, "true", false, "", "", "", 0, "", 0, false); + } catch (ApiException e) { + log.error("Cannot list pods in namespace {}: {} ", namespace, e.getMessage(), e); + return new V1PodList(); + } + } + + @Override + public PlatformHealth getPlatformHealth () { + if (namespaces.stream().map(this::canListPodsInNamespace).anyMatch(canList -> canList.equals(false))) { + return PlatformHealth.FAILED; + } + if (namespaces.stream() + .map(this::listAllPodsInNamespace) + .map(V1PodList::getItems) + .flatMap(List::stream) + .collect(Collectors.toList()) + .isEmpty()) { + log.warn("No PODs detected in specified namespaces {}", namespaces); + return PlatformHealth.DEGRADED; + } + return PlatformHealth.OK; } @Override @@ -151,25 +175,28 @@ public PlatformLevel getPlatformLevel () { return PlatformLevel.PAAS; } - @Override - public PlatformHealth getPlatformHealth () { + private boolean canListPodsInNamespace (String namespace) { try { - V1PodList pods = listAllPodsInNamespace(); - return (!pods.getItems().isEmpty()) ? PlatformHealth.OK : PlatformHealth.DEGRADED; - } catch (ApiException e) { - log.error("Kubernetes Platform health check failed", e); - return PlatformHealth.FAILED; + getCoreV1Api().listNamespacedPod(namespace, "true", false, "", "", "", 0, "", 0, false); + } catch (Exception e) { + log.error("Cannot list pods in namespace {}: {} ", namespace, e.getMessage(), e); + return false; } + return true; } @Override protected List generateRoster () { final List containerList = new ArrayList<>(); try { - V1PodList pods = listAllPodsInNamespace(); - containerList.addAll(pods.getItems().stream().map(this::fromKubernetesAPIPod).collect(Collectors.toSet())); + List pods = namespaces.stream() + .map(this::listAllPodsInNamespace) + .map(V1PodList::getItems) + .flatMap(List::stream) + .collect(Collectors.toList()); + containerList.addAll(pods.stream().map(this::fromKubernetesAPIPod).collect(Collectors.toSet())); return containerList; - } catch (ApiException e) { + } catch (Exception e) { log.error("Could not generate Kubernetes roster", e); return containerList; } @@ -178,7 +205,7 @@ protected List generateRoster () { @Override public boolean isContainerRecycled (Container container) { KubernetesPodContainer kubernetesPodContainer = (KubernetesPodContainer) container; - if (podExists(kubernetesPodContainer).orElse(false)) return isContainerRestarted(kubernetesPodContainer, + if (podExists(kubernetesPodContainer)) return isContainerRestarted(kubernetesPodContainer, ((KubernetesPodContainer) container).getTargetedSubcontainer()); return isDesiredReplicas(kubernetesPodContainer); } @@ -195,11 +222,11 @@ KubernetesPodContainer fromKubernetesAPIPod (V1Pod pod) { .withKubernetesPlatform(this) .isBackedByController(CollectionUtils.isNotEmpty(pod.getMetadata() .getOwnerReferences())) - .withOwnerKind(Optional.of(pod.getMetadata().getOwnerReferences()) + .withOwnerKind(Optional.ofNullable(pod.getMetadata().getOwnerReferences()) .flatMap(list -> list.stream().findFirst()) .map(V1OwnerReference::getKind) .orElse("")) - .withOwnerName(Optional.of(pod.getMetadata().getOwnerReferences()) + .withOwnerName(Optional.ofNullable(pod.getMetadata().getOwnerReferences()) .flatMap(list -> list.stream().findFirst()) .map(V1OwnerReference::getName) .orElse("")) @@ -210,6 +237,8 @@ KubernetesPodContainer fromKubernetesAPIPod (V1Pod pod) { .flatMap(Collection::stream) .map(V1Container::getName) .collect(Collectors.toList())) + .withParentNode(Optional.ofNullable(pod.getSpec().getNodeName()) + .orElse(UNKNOWN_PARENT_NODE)) .build(); log.info("Found new Kubernetes Pod Container {}", v(DATADOG_CONTAINER_KEY, container)); containerManager.offer(container); @@ -247,7 +276,7 @@ private boolean isContainerRestarted (KubernetesPodContainer container, String s } public ContainerHealth replicaSetRecovered (KubernetesPodContainer kubernetesPodContainer) { - return isDesiredReplicas(kubernetesPodContainer) && !podExists(kubernetesPodContainer).orElse(false) ? ContainerHealth.NORMAL : ContainerHealth.RUNNING_EXPERIMENT; + return isDesiredReplicas(kubernetesPodContainer) && !podExists(kubernetesPodContainer) ? ContainerHealth.NORMAL : ContainerHealth.RUNNING_EXPERIMENT; } /** diff --git a/chaosengine-experiments/chaosengine-kubernetes/src/test/java/com/thales/chaos/platform/impl/KubernetesPlatformTest.java b/chaosengine-experiments/chaosengine-kubernetes/src/test/java/com/thales/chaos/platform/impl/KubernetesPlatformTest.java index 218889ca4..b5e645197 100644 --- a/chaosengine-experiments/chaosengine-kubernetes/src/test/java/com/thales/chaos/platform/impl/KubernetesPlatformTest.java +++ b/chaosengine-experiments/chaosengine-kubernetes/src/test/java/com/thales/chaos/platform/impl/KubernetesPlatformTest.java @@ -34,6 +34,7 @@ import io.kubernetes.client.openapi.models.*; import junit.framework.TestCase; import org.apache.http.HttpStatus; +import org.hamcrest.collection.IsIterableContainingInAnyOrder; import org.joda.time.DateTime; import org.junit.Assert; import org.junit.Before; @@ -79,22 +80,29 @@ public class KubernetesPlatformTest { @Mock private AppsV1Api appsV1Api; - @Before - public void setUp () { - doReturn(coreApi).when(platform).getCoreApi(); - doReturn(coreV1Api).when(platform).getCoreV1Api(); - doReturn(appsV1Api).when(platform).getAppsV1Api(); - platform.setNamespace(NAMESPACE_NAME); + private static V1PodList getV1PodList (boolean isBackedByController, int numberOfPods) { + List ownerReferences = null; + if (isBackedByController) { + ownerReferences = new ArrayList<>(); + ownerReferences.add(new V1OwnerReferenceBuilder().withNewController("mycontroller").build()); + } + V1ObjectMeta metadata = new V1ObjectMetaBuilder().withUid(randomUUID().toString()) + .withName(POD_NAME) + .withNamespace(NAMESPACE_NAME) + .withLabels(new HashMap<>()) + .withOwnerReferences(ownerReferences) + .build(); + V1Pod pod = new V1Pod(); + pod.setMetadata(metadata); + pod.setSpec(new V1PodSpec().containers(Collections.singletonList(new V1Container().name(randomUUID().toString())))); + V1PodList list = new V1PodList(); + for (int i = 0; i < numberOfPods; i++) list.addItemsItem(pod); + return list; } @Test public void testPodWithoutOwnerCannotBeTested () throws Exception { - when(coreV1Api.listNamespacedPod(anyString(), - anyString(), - anyBoolean(), - anyString(), - anyString(), - anyString(), + when(coreV1Api.listNamespacedPod(anyString(), anyString(), anyBoolean(), anyString(), anyString(), anyString(), anyInt(), anyString(), anyInt(), @@ -139,31 +147,14 @@ public void testContainerHealthWithException () throws ApiException { anyString(), anyInt(), anyBoolean())).thenThrow(new ApiException()); - assertEquals("Error while checking container presence", - ContainerHealth.RUNNING_EXPERIMENT, - platform.checkHealth((KubernetesPodContainer) platform.getRoster().get(0))); } - @Test - public void testPodExists () throws ApiException { - V1PodList v1PodList = getV1PodList(true); - V1Pod pod = v1PodList.getItems().get(0); - KubernetesPodContainer kubernetesPodContainer = platform.fromKubernetesAPIPod(pod); - when(coreV1Api.listNamespacedPod(anyString(), - anyString(), - anyBoolean(), - anyString(), - anyString(), - anyString(), - anyInt(), - anyString(), - anyInt(), - anyBoolean())).thenReturn(v1PodList) - .thenReturn(new V1PodList()) - .thenThrow(new ApiException(new IOException())); - assertTrue("POD exists", platform.podExists(kubernetesPodContainer).orElseThrow()); - assertFalse("POD does not exist", platform.podExists(kubernetesPodContainer).orElseThrow()); - assertTrue("IO Exception", platform.podExists(kubernetesPodContainer).isEmpty()); + @Before + public void setUp () { + doReturn(coreApi).when(platform).getCoreApi(); + doReturn(coreV1Api).when(platform).getCoreV1Api(); + doReturn(appsV1Api).when(platform).getAppsV1Api(); + platform.setNamespaces(NAMESPACE_NAME); } @Test @@ -197,6 +188,21 @@ public void testPlatformHealth () throws ApiException { assertEquals(PlatformHealth.OK, platform.getPlatformHealth()); } + @Test + public void testPlatformHealthCannotListPods () throws ApiException { + when(coreV1Api.listNamespacedPod(anyString(), + anyString(), + anyBoolean(), + anyString(), + anyString(), + anyString(), + anyInt(), + anyString(), + anyInt(), + anyBoolean())).thenThrow(new ApiException()); + assertEquals(PlatformHealth.FAILED, platform.getPlatformHealth()); + } + @Test public void testPodWithOwnerCanBeTested () throws Exception { when(coreV1Api.listNamespacedPod(anyString(), @@ -365,34 +371,10 @@ public void testCheckDesiredReplicasReplicationController () throws ApiException } @Test - public void testPlatformHealthNotAvailable () throws ApiException { - when(coreV1Api.listNamespacedPod(anyString(), - anyString(), - anyBoolean(), - anyString(), - anyString(), - anyString(), - anyInt(), - anyString(), - anyInt(), - anyBoolean())).thenThrow(new ApiException()); - assertEquals(PlatformHealth.FAILED, platform.getPlatformHealth()); - } - - @Test - public void testContainerHealthDoesNotExist () throws ApiException { - V1PodList list = new V1PodList(); - V1Pod pod = mock(V1Pod.class); - V1ObjectMeta metadata = mock(V1ObjectMeta.class); - V1PodSpec spec = mock(V1PodSpec.class); - when(metadata.getUid()).thenReturn(randomUUID().toString()); - when(pod.getMetadata()).thenReturn(metadata); - when(pod.getSpec()).thenReturn(spec); - list.addItemsItem(pod); - KubernetesPodContainer kubernetesPodContainer = KubernetesPodContainer.builder() - .withUUID(randomUUID().toString()) - .withOwnerKind("") - .build(); + public void testPodExists () throws ApiException { + V1PodList v1PodList = getV1PodList(true); + V1Pod pod = v1PodList.getItems().get(0); + KubernetesPodContainer kubernetesPodContainer = platform.fromKubernetesAPIPod(pod); when(coreV1Api.listNamespacedPod(anyString(), anyString(), anyBoolean(), @@ -402,8 +384,12 @@ public void testContainerHealthDoesNotExist () throws ApiException { anyInt(), anyString(), anyInt(), - anyBoolean())).thenReturn(list); - assertEquals(ContainerHealth.DOES_NOT_EXIST, platform.checkHealth(kubernetesPodContainer)); + anyBoolean())).thenReturn(v1PodList) + .thenReturn(new V1PodList()) + .thenThrow(new ApiException(new IOException())); + assertTrue("POD exists", platform.podExists(kubernetesPodContainer)); + assertFalse("POD does not exist", platform.podExists(kubernetesPodContainer)); + assertFalse("IO Exception", platform.podExists(kubernetesPodContainer)); } @Test @@ -505,28 +491,32 @@ public void testContainerHealthWithSeveralContainerOneUnhealthy () throws ApiExc platform.checkHealth((KubernetesPodContainer) platform.getRoster().get(0))); } - private static V1PodList getV1PodList (boolean isBackedByController, int numberOfPods) { - List ownerReferences = new ArrayList<>(); - if (isBackedByController) { - ownerReferences.add(new V1OwnerReferenceBuilder().withNewController("mycontroller").build()); - } - V1ObjectMeta metadata = new V1ObjectMetaBuilder().withUid(randomUUID().toString()) - .withName(POD_NAME) - .withNamespace(NAMESPACE_NAME) - .withLabels(new HashMap<>()) - .withOwnerReferences(ownerReferences) - .build(); - V1Pod pod = new V1Pod(); - pod.setMetadata(metadata); - pod.setSpec(new V1PodSpec().containers(Collections.singletonList(new V1Container().name(randomUUID().toString())))); - V1PodList list = new V1PodList(); - for (int i = 0; i < numberOfPods; i++) list.addItemsItem(pod); - return list; - } - @Test - public void testGetNamespace () { - assertEquals("mynamespace", platform.getNamespace()); + public void testContainerHealthDoesNotExist () throws ApiException { + V1PodList list = new V1PodList(); + V1Pod pod = mock(V1Pod.class); + V1ObjectMeta metadata = mock(V1ObjectMeta.class); + V1PodSpec spec = mock(V1PodSpec.class); + when(metadata.getUid()).thenReturn(randomUUID().toString()); + when(pod.getMetadata()).thenReturn(metadata); + when(pod.getSpec()).thenReturn(spec); + list.addItemsItem(pod); + KubernetesPodContainer kubernetesPodContainer = KubernetesPodContainer.builder() + .withNamespace(NAMESPACE_NAME) + .withUUID(randomUUID().toString()) + .withOwnerKind("") + .build(); + when(coreV1Api.listNamespacedPod(anyString(), + anyString(), + anyBoolean(), + anyString(), + anyString(), + anyString(), + anyInt(), + anyString(), + anyInt(), + anyBoolean())).thenReturn(list); + assertEquals(ContainerHealth.DOES_NOT_EXIST, platform.checkHealth(kubernetesPodContainer)); } private static V1PodList getV1PodList (boolean isBackedByController) { @@ -847,6 +837,7 @@ public void testIsContainerRecycled () throws ApiException { String uid = randomUUID().toString(); String containerName = randomUUID().toString(); KubernetesPodContainer kubernetesPodContainer = spy(KubernetesPodContainer.builder() + .withNamespace(NAMESPACE_NAME) .withUUID(uid) .withSubcontainers(List.of( containerName)) @@ -922,7 +913,7 @@ public void testIsContainerRecycledAPIError () throws ApiException { .withOwnerKind(REPLICA_SET.toString()) .withSubcontainers(Set.of(subContainer)) .build(); - doReturn(Optional.of(Boolean.TRUE)).when(platform).podExists(kubernetesPodContainer); + doReturn(true).when(platform).podExists(kubernetesPodContainer); doThrow(new ApiException()).when(coreV1Api).readNamespacedPodStatus(any(), any(), any()); platform.isContainerRecycled(kubernetesPodContainer); } @@ -948,7 +939,16 @@ public void testRecycleContainer () throws ApiException { public void testGetConnectedShellClient () throws IOException, ApiException { KubernetesPodContainer kubernetesPodContainer = mock(KubernetesPodContainer.class); platform.getConnectedShellClient(kubernetesPodContainer); + } + @Test + public void testSetNamespaces () { + platform.setNamespaces(""); + assertThat(platform.getNamespaces(), + IsIterableContainingInAnyOrder.containsInAnyOrder(KubernetesPlatform.DEFAULT_NAMESPACE)); + platform.setNamespaces("default,application"); + assertThat(platform.getNamespaces(), + IsIterableContainingInAnyOrder.containsInAnyOrder("default", "application")); } @Configuration @@ -966,7 +966,7 @@ KubernetesPlatform kubernetesPlatform () { } private class TestProcess extends Process { - private InputStream is; + private final InputStream is; public TestProcess (InputStream is) { this.is = is; diff --git a/docs/markdown/Experiment_Modules/kubernetes_experiments.md b/docs/markdown/Experiment_Modules/kubernetes_experiments.md index d1555b4bc..4135893a4 100644 --- a/docs/markdown/Experiment_Modules/kubernetes_experiments.md +++ b/docs/markdown/Experiment_Modules/kubernetes_experiments.md @@ -14,7 +14,7 @@ The official Kubernetes Java Client is used to interact with the cluster. | | | | --- | --- | | Resource | | -| Version | 9.0.2 | +| Version | 10.0.0 | | Maven Repositories | | ## Configuration @@ -26,7 +26,7 @@ Environment variables that control how the Chaos Engine interacts with Kubernete | kubernetes | The presence of this key enables Kubernetes module. | N/A | Yes | | kubernetes.url | Kubernetes server API url e.g. | None | Yes | | kubernetes.token | JWT token assigned to service account. You can get the value by running `kubectl describe secret name_of_your_secret` | None | Yes | -| kubernetes.namespace | K8S namespace where experiments should be performed | `default` | Yes | +| kubernetes.namespaces | Comma-separated list of namespaces where experiments should be performed | `default` | Yes | | kubernetes.debug | Enables debug log of Kubernetes java client | `false` | No | | kubernetes.validateSSL | Enables validation of sever side certificates | `false` | No | @@ -36,6 +36,7 @@ A service account with a role binding needs to be created in order to access spe Please replace the {{namespace}} fillers with the appropriate values and apply to your cluster. +### Experiments on single namespace **chaos-engine-service-account.yaml** ```yaml @@ -110,7 +111,86 @@ subjects: namespace: {{namespace}} ``` -You can retrieve the token by runningĀ `kubectl describe secret chaos-engine -n {{namespace}}` +You can retrieve the token by running `kubectl describe secret chaos-engine -n {{namespace}}` + +### Experiments on multiple namespaces + +When your experiment targets are located in multiple namespaces, +you need to bind roles allowing access to appropriate namespace to your service account. +Or you can simply create a cluster role and binding by running below yaml. + + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: chaos-engine-crole +rules: +- apiGroups: + - apps + resources: + - daemonsets + - daemonsets/status + - deployments + - deployments/status + - replicasets + - replicasets/status + - statefulsets + - statefulsets/status + verbs: + - get + - list +- apiGroups: + - "" + resources: + - pods + verbs: + - delete + + +- apiGroups: + - "" + resources: + - pods + - pods/status + - replicationcontrollers/status + verbs: + - get + - list + +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create + - get + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: chaos-engine-rolebinding +roleRef: + kind: ClusterRole + name: chaos-engine-crole + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: chaos-engine-serviceaccount + namespace: {{namespace}} + +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: chaos-engine-serviceaccount + namespace: {{namespace}} + +``` + ### Verify Service Account Setting