Skip to content

Commit

Permalink
Add collecting of cluster wide resources
Browse files Browse the repository at this point in the history
Signed-off-by: David Kornel <[email protected]>
  • Loading branch information
kornys committed Jun 3, 2024
1 parent 2047436 commit 33c6347
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 30 deletions.
34 changes: 29 additions & 5 deletions test-frame-log-collector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import io.skodjob.testframe.LogCollector;
import io.skodjob.testframe.LogCollectorBuilder;

LogCollector logCollector = new LogCollectorBuilder()
.withResources("secret", "deployment", "my-custom-resource")
.withNamespacedResources("secret", "deployment", "my-custom-resource")
.withRootFolderPath("/path/to/logs/folder")
.build();
```
Expand All @@ -66,7 +66,7 @@ import io.skodjob.testframe.LogCollector;
import io.skodjob.testframe.LogCollectorBuilder;

LogCollector logCollector = new LogCollectorBuilder()
.withResources("secret", "deployment", "my-custom-resource")
.withNamespacedResources("secret", "deployment", "my-custom-resource")
.withRootFolderPath("/path/to/logs/folder")
.build();

Expand All @@ -92,7 +92,7 @@ import io.skodjob.testframe.LogCollector;
import io.skodjob.testframe.LogCollectorBuilder;

LogCollector logCollector = new LogCollectorBuilder()
.withResources("secret", "deployment", "my-custom-resource")
.withNamespacedResources("secret", "deployment", "my-custom-resource")
.withRootFolderPath("/path/to/logs/folder")
.build();

Expand Down Expand Up @@ -127,17 +127,41 @@ import io.skodjob.testframe.LogCollector;
import io.skodjob.testframe.LogCollectorBuilder;

LogCollector logCollector = new LogCollectorBuilder()
.withResources("secret", "deployment", "my-custom-resource")
.withNamespacedResources("secret", "deployment", "my-custom-resource")
.withRootFolderPath("/path/to/logs/folder")
.build();

public static void collectFromNamespaces() {
logCollector.collectFromNamespacesWithLabels(new LabelSelectorBuilder()
.withMatchLabels(Map.of("my-label", "my-value"))
.withMatchLabels(Map.of("my-label", "my-value"))
);
}
```

#### 4. Collect cluster wide resource lists

Collect cluster wide resource lists yaml like `nodes`, `pvs`:

```java
import io.fabric8.kubernetes.api.model.LabelSelectorBuilder;
import io.skodjob.testframe.LogCollector;
import io.skodjob.testframe.LogCollectorBuilder;

LogCollector logCollector = new LogCollectorBuilder()
.withClusterWideResources("node", "pv")
.withRootFolderPath("/path/to/logs/folder")
.build();

public static void collectClusterWideRes() {
logCollector.collectClusterWideResources();
}
```
the logs path will then look like this:
```bash
/path/to/logs/folder
├── node.yaml
└── pv.yaml
```
The tree path will look similarly to above examples, there will be folders for Namespaces matching the specified labels.

### Specifying additional folder path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@
*/
public class LogCollector {
private static final Logger LOGGER = LogManager.getLogger(KubeResourceManager.class);
protected final List<String> resources;
protected final List<String> namespacedResources;
protected final List<String> clusterWideResources;
protected String rootFolderPath;
private KubeCmdClient<Kubectl> kubeCmdClient = new Kubectl();
private KubeCmdClient<?> kubeCmdClient = new Kubectl();
private KubeClient kubeClient = new KubeClient();

/**
Expand All @@ -39,12 +40,23 @@ public class LogCollector {
* @param builder {@link LogCollectorBuilder} with configuration for {@link LogCollector}
*/
public LogCollector(LogCollectorBuilder builder) {
this.resources = builder.getResources() == null ? Collections.emptyList() : builder.getResources();
this.namespacedResources = builder.getNamespacedResources() == null ?
Collections.emptyList() : builder.getNamespacedResources();
this.clusterWideResources = builder.getClusterWideResources() == null ?
Collections.emptyList() : builder.getClusterWideResources();

if (builder.getRootFolderPath() == null) {
throw new RuntimeException("rootFolderPath should be filled, but it's empty");
}

if (builder.getKubeClient() != null) {
this.setKubeClient(builder.getKubeClient());
}

if (builder.getKubeCmdClient() != null) {
this.setKubeCmdClient(builder.getKubeCmdClient());
}

this.rootFolderPath = builder.getRootFolderPath();
}

Expand All @@ -64,7 +76,7 @@ public LogCollector(LogCollectorBuilder builder) {
*
* @param kubeCmdClient {@link KubeCmdClient} - usually mocked.
*/
/* test */ public void setKubeCmdClient(KubeCmdClient<Kubectl> kubeCmdClient) {
/* test */ public void setKubeCmdClient(KubeCmdClient<?> kubeCmdClient) {
this.kubeCmdClient = kubeCmdClient;
}

Expand Down Expand Up @@ -129,6 +141,14 @@ public void collectFromNamespace(String namespaceName) {
collectFromNamespaceToFolder(namespaceName, null);
}

/**
* Method that collects YAML of cluster wide resources
* {@link #rootFolderPath}.
*/
public void collectClusterWideResources() {
collectClusterWideResourcesToFolder("");
}

/**
* Method that collects all logs and YAML files from specified Namespace, collected into
* {@link #rootFolderPath} with {@param folderPath}.
Expand All @@ -151,6 +171,22 @@ public void collectFromNamespaceToFolder(String namespaceName, String folderPath
}
}

/**
* Method that collects YAML of cluster wide resources
* {@link #rootFolderPath} with {@param folderPath}.
*
* @param folderPath folder path for the log collection
*/
public void collectClusterWideResourcesToFolder(String folderPath) {
String path = LogCollectorUtils.getFolderPath(rootFolderPath, folderPath);
createLogDirOnPath(path);
clusterWideResources.forEach(resource -> {
writeDataToFile(Paths.get(path, resource + ".yaml").toString(),
kubeCmdClient.exec(false, false,
"get", resource, "-o", "yaml").out());
});
}

/**
* Method for collecting logs from all Pods (and their containers) in specified Namespace.
* At the start, it creates a folder in the Namespace dir for the `pod` resource.
Expand Down Expand Up @@ -261,7 +297,7 @@ public void collectEventsFromNamespace(String namespaceName, String namespaceFol
* @param namespaceFolderPath path to Namespace folder where the resource directories will be created
*/
private void collectResourcesDescInNamespace(String namespaceName, String namespaceFolderPath) {
resources.forEach(resource ->
namespacedResources.forEach(resource ->
collectDescriptionOfResourceInNamespace(namespaceName, namespaceFolderPath, resource));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
*/
package io.skodjob.testframe;

import io.skodjob.testframe.clients.KubeClient;
import io.skodjob.testframe.clients.cmdClient.KubeCmdClient;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -14,7 +17,10 @@
public class LogCollectorBuilder {

private String rootFolderPath;
private List<String> resources;
private List<String> namespacedResources;
private List<String> clusterWideResources;
private KubeClient kubeClient;
private KubeCmdClient<?> kubeCmdClient;

/**
* Constructor for creating {@link LogCollectorBuilder} with parameters from
Expand All @@ -24,7 +30,8 @@ public class LogCollectorBuilder {
*/
public LogCollectorBuilder(LogCollector logCollector) {
this.rootFolderPath = logCollector.rootFolderPath;
this.resources = logCollector.resources;
this.namespacedResources = logCollector.namespacedResources;
this.clusterWideResources = logCollector.clusterWideResources;
}

/**
Expand All @@ -47,21 +54,62 @@ public LogCollectorBuilder withRootFolderPath(String rootFolderPath) {
}

/**
* Method for setting the resources, which YAML descriptions should be collected as part of the log collection
* Method for setting the namespaced resources, which YAML
* descriptions should be collected as part of the log collection
*
* @param resources array of resources
*
* @return {@link LogCollectorBuilder} object
*/
public LogCollectorBuilder withResources(String... resources) {
this.resources = Arrays.stream(resources).toList()
public LogCollectorBuilder withNamespacedResources(String... resources) {
this.namespacedResources = Arrays.stream(resources).toList()
.stream()
.filter(resource -> !resource.equals(CollectorConstants.POD) && !resource.equals(CollectorConstants.PODS))
.collect(Collectors.toList());

return this;
}

/**
* Method for setting the cluster wide resources, which YAML
* descriptions should be collected as part of the log collection
*
* @param resources array of resources
*
* @return {@link LogCollectorBuilder} object
*/
public LogCollectorBuilder withClusterWideResources(String... resources) {
this.clusterWideResources = Arrays.stream(resources).toList()
.stream()
.filter(resource ->
!resource.equals(CollectorConstants.POD) && !resource.equals(CollectorConstants.PODS))
.collect(Collectors.toList());

return this;
}

/**
* Setter for kubeClient
*
* @param kubeClient kubeClientInstance
* @return {@link LogCollectorBuilder} object
*/
public LogCollectorBuilder withKubeClient(KubeClient kubeClient) {
this.kubeClient = kubeClient;
return this;
}

/**
* Setter for kubeCmdClient
*
* @param kubeCmdClient kubeClientInstance
* @return {@link LogCollectorBuilder} object
*/
public LogCollectorBuilder withKubeCmdClient(KubeCmdClient<?> kubeCmdClient) {
this.kubeCmdClient = kubeCmdClient;
return this;
}

/**
* Getter returning currently configured {@link #rootFolderPath}
*
Expand All @@ -72,12 +120,39 @@ public String getRootFolderPath() {
}

/**
* Getter returning currently configured {@link #resources} in {@link List} object
* Getter returning currently configured {@link #namespacedResources} in {@link List} object
*
* @return {@link #namespacedResources}
*/
public List<String> getNamespacedResources() {
return this.namespacedResources;
}

/**
* Getter returning currently configured {@link #clusterWideResources} in {@link List} object
*
* @return {@link #clusterWideResources}
*/
public List<String> getClusterWideResources() {
return this.clusterWideResources;
}

/**
* Getter for kubeClient
*
* @return {@link #kubeClient}
*/
public KubeClient getKubeClient() {
return this.kubeClient;
}

/**
* Getter for kubeCmdClient
*
* @return {@link #resources}
* @return {@link #kubeCmdClient}
*/
public List<String> getResources() {
return this.resources;
public KubeCmdClient<?> getKubeCmdClient() {
return this.kubeCmdClient;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io.skodjob.testframe.annotations.TestVisualSeparator;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.Collections;

import static org.junit.jupiter.api.Assertions.assertEquals;
Expand All @@ -15,9 +14,9 @@ public class LogCollectorBuilderTest {
@Test
void testPassingPodAndPodsAsResourcesToLogCollectorBuilder() {
LogCollectorBuilder logCollectorBuilder = new LogCollectorBuilder()
.withResources("pod", "pods");
.withNamespacedResources("pod", "pods");

assertEquals(Collections.emptyList(), logCollectorBuilder.getResources());
assertEquals(Collections.emptyList(), logCollectorBuilder.getNamespacedResources());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public void setup() {
mockNamespaceOperation = mock(NonNamespaceOperation.class);

logCollector = new LogCollectorBuilder()
.withResources(SECRET, DEPLOYMENT, CONFIG_MAP)
.withNamespacedResources(SECRET, DEPLOYMENT, CONFIG_MAP)
.withRootFolderPath(pathToRoot)
.build();
}
Expand All @@ -91,10 +91,9 @@ public void setRootPathForCollector() {

logCollector = new LogCollectorBuilder(logCollector)
.withRootFolderPath(getFolderPathForTest())
.withKubeCmdClient(mockCmdClient)
.withKubeClient(mockClient)
.build();

logCollector.setKubeCmdClient(mockCmdClient);
logCollector.setKubeClient(mockClient);
}

/**
Expand Down Expand Up @@ -258,7 +257,7 @@ void testCollectFromNonExistentNamespace() {
void testChangingCustomResources() throws IOException {
LogCollector localLogCollector = new LogCollectorBuilder()
.withRootFolderPath(getFolderPathForTest())
.withResources(SECRET)
.withNamespacedResources(SECRET)
.build();

localLogCollector.setKubeClient(mockClient);
Expand Down Expand Up @@ -292,7 +291,7 @@ void testChangingCustomResources() throws IOException {

localLogCollector = new LogCollectorBuilder(localLogCollector)
.withRootFolderPath(getFolderPathForTest())
.withResources(CONFIG_MAP, DEPLOYMENT)
.withNamespacedResources(CONFIG_MAP, DEPLOYMENT)
.build();

localLogCollector.setKubeClient(mockClient);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public class LogCollectorIT extends AbstractIT {
private final String folderRoot = "/tmp/log-collector-examples";
private final String[] resourcesToBeCollected = new String[] {"secret", "configmap", "deployment"};
private final LogCollector logCollector = new LogCollectorBuilder()
.withResources(resourcesToBeCollected)
.withNamespacedResources(resourcesToBeCollected)
.withClusterWideResources("nodes")
.withRootFolderPath(folderRoot)
.build();

Expand Down Expand Up @@ -144,6 +145,7 @@ void testCollectFromMultipleNamespacesWithDifferentResources() {
);

logCollector.collectFromNamespacesToFolder(List.of(namespaceName1, namespaceName2), folderPath);
logCollector.collectClusterWideResourcesToFolder(folderPath);

List<String> podNames = KubeResourceManager.getKubeClient()
.listPods(namespaceName1).stream().map(pod -> pod.getMetadata().getName()).toList();
Expand All @@ -157,7 +159,9 @@ void testCollectFromMultipleNamespacesWithDifferentResources() {
File[] namespaceFolders = rootFolder.listFiles();

assertNotNull(namespaceFolders);
assertEquals(2, namespaceFolders.length);
assertEquals(3, namespaceFolders.length);

assertTrue(rootFolder.toPath().resolve("nodes.yaml").toFile().exists());

Arrays.stream(namespaceFolders).forEach(namespaceFolder -> {
List<File> namespaceFolderFiles = Arrays.asList(Objects.requireNonNull(namespaceFolder.listFiles()));
Expand Down

0 comments on commit 33c6347

Please sign in to comment.