Skip to content
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
32 changes: 30 additions & 2 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -181,7 +181,7 @@ Equivalent of [`helm list`](https://helm.sh/docs/helm/helm_list/).
Lists all the releases for a specified namespace (uses current namespace context if namespace not specified).

``` java
Helm.list()
List<Release> releases = Helm.list()
// Optionally specify the Kubernetes namespace to list the releases from
.withNamespace("namespace")
// Optionally specify the path to the kubeconfig file to use for CLI requests
Expand DownExpand Up@@ -347,7 +347,7 @@ Equivalent of [`helm repo list`](https://helm.sh/docs/helm/helm_repo_list/).
List chart repositories.

``` java
Helm.repo().list()
List<Repository> respositories = Helm.repo().list()
// Optionally set the path to the file containing repository names and URLs
// Defaults to "~/.config/helm/repositories.yaml"
.withRepositoryConfig(Paths.get("path", "to", "config"))
Expand All@@ -372,6 +372,34 @@ Helm.repo().remove()
.call();
```

### Search

Equivalent of [`helm search`](https://helm.sh/docs/helm/helm_search/).

This command provides the ability to search for Helm charts in various places including the Artifact Hub and the repositories you have added.

#### Repo

Equivalent of [`helm search repo`](https://helm.sh/docs/helm/helm_search_repo/).

Search repositories for a keyword in charts.

``` java
List<SearchResult> results = Helm.search().repo()
// Optionally set the path to the file containing repository names and URLs
// Defaults to "~/.config/helm/repositories.yaml"
.withRepositoryConfig(Paths.get("path", "to", "config"))
// Optionally set the keyword to match against the repo name, chart name, chart keywords, and description.
.withKeyword("keyword")
// Optionally use regular expressions for searching.
.regexp()
// Optionally search for development versions too (alpha, beta, and release candidate releases).
.devel()
// Optionally search using semantic versioning constraints
.withVersion(">=1.0.0")
.call();
```

### Show

Equivalent of [`helm show`](https://helm.sh/docs/helm/helm_show/).
Expand Down
10 changes: 10 additions & 0 deletions helm-java/src/main/java/com/marcnuri/helm/Helm.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -113,6 +113,16 @@ public static RepoCommand repo() {
return new RepoCommand(HelmLibHolder.INSTANCE);
}

/**
* This command provides the ability to search for Helm charts in various places including the Artifact Hub
* and the repositories you have added.
*
* @return the {@link SearchCommand} command.
*/
public static SearchCommand search() {
return new SearchCommand(HelmLibHolder.INSTANCE);
}

/**
* This command shows information about a chart.
*
Expand Down
111 changes: 111 additions & 0 deletions helm-java/src/main/java/com/marcnuri/helm/SearchCommand.java
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
package com.marcnuri.helm;

import com.marcnuri.helm.jni.HelmLib;
import com.marcnuri.helm.jni.Result;
import com.marcnuri.helm.jni.SearchOptions;

import java.nio.file.Path;
import java.util.List;
import java.util.function.Function;

public class SearchCommand {

private final HelmLib helmLib;

public SearchCommand(HelmLib helmLib) {
this.helmLib = helmLib;
}

/**
* Search repositories for a keyword in charts.
*
* @return the {@link SearchCommand.SearchSubcommand} subcommand.
*/
public SearchCommand.SearchSubcommand<List<SearchResult>> repo() {
return new SearchCommand.SearchSubcommand<>(helmLib, hl -> hl::SearchRepo, SearchResult::parse);
}

public static final class SearchSubcommand<T> extends HelmCommand<T> {

private final Function<HelmLib, Function<SearchOptions, Result>> callable;
private final Function<Result, T> transformer;
private Path repositoryConfig;
private String keyword;
private boolean regexp;
private boolean devel;
private String version;

SearchSubcommand(HelmLib helmLib, Function<HelmLib, Function<SearchOptions, Result>> callable, Function<Result, T> transformer) {
super(helmLib);
this.callable = callable;
this.transformer = transformer;
}

@Override
public T call() {
return transformer.apply(run(hl -> callable.apply(hl).apply(new SearchOptions(
toString(repositoryConfig),
keyword,
toInt(regexp),
toInt(devel),
version
))));
}

/**
* Path to the file containing repository names and URLs
* (default "~/.config/helm/repositories.yaml")
*
* @param repositoryConfig a {@link Path} to the repository configuration file.
* @return this {@link SearchCommand.SearchSubcommand} instance.
*/
public SearchCommand.SearchSubcommand<T> withRepositoryConfig(Path repositoryConfig) {
this.repositoryConfig = repositoryConfig;
return this;
}

/**
* The keyword(s) to match against the repo name, chart name, chart keywords, and description.
*
* @param keyword the keyword to search for.
* @return this {@link SearchCommand.SearchSubcommand} instance.
*/
public SearchCommand.SearchSubcommand<T> withKeyword(String keyword) {
this.keyword = keyword;
return this;
}

/**
* Use regular expressions for searching.
*
* @return this {@link SearchCommand.SearchSubcommand} instance.
*/
public SearchCommand.SearchSubcommand<T> regexp() {
this.regexp = true;
return this;
}

/**
* Search for development versions too (alpha, beta, and release candidate releases).
*
* <p>Equivalent to withVersion '&gt;0.0.0-0'.
*
* @return this {@link SearchCommand.SearchSubcommand} instance.
*/
public SearchCommand.SearchSubcommand<T> devel() {
this.devel = true;
return this;
}

/**
* Search using semantic versioning constraints (e.g. &gt;1.0.0, &lt;2.0.0, &gt;=1.0.0, &lt;=2.0.0).
*
* @param version the version to search for.
* @return this {@link SearchCommand.SearchSubcommand} instance.
*/
public SearchCommand.SearchSubcommand<T> withVersion(String version) {
this.version = version;
return this;
}
}
}
70 changes: 70 additions & 0 deletions helm-java/src/main/java/com/marcnuri/helm/SearchResult.java
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
package com.marcnuri.helm;

import com.marcnuri.helm.jni.Result;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.marcnuri.helm.HelmCommand.parseUrlEncodedLines;

public class SearchResult {

private final String name;
private final int score;
private final String chartVersion;
private final String appVersion;
private final String description;
private final String keywords;

public SearchResult(String name, int score, String chartVersion, String appVersion, String description, String keywords) {
this.name = name;
this.score = score;
this.chartVersion = chartVersion;
this.appVersion = appVersion;
this.description = description;
this.keywords = keywords;
}

public String getName() {
return name;
}

public int getScore() {
return score;
}

public String getChartVersion() {
return chartVersion;
}

public String getAppVersion() {
return appVersion;
}

public String getDescription() {
return description;
}

public String getKeywords() {
return keywords;
}

public static List<SearchResult> parse(Result result) {
if (result == null) {
throw new IllegalArgumentException("Result cannot be null");
}
final List<SearchResult> searchResults = new ArrayList<>();
for (Map<String, String> entries : parseUrlEncodedLines(result.out)) {
searchResults.add(new SearchResult(
entries.get("name"),
Integer.parseInt(entries.get("score")),
entries.getOrDefault("chartVersion", ""),
entries.getOrDefault("appVersion", ""),
entries.getOrDefault("description", ""),
entries.getOrDefault("keywords", "")
));
}
return searchResults;
}
}
77 changes: 77 additions & 0 deletions helm-java/src/test/java/com/marcnuri/helm/HelmSearchTest.java
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
package com.marcnuri.helm;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.net.URI;
import java.nio.file.Path;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

public class HelmSearchTest {

@Nested
class SearchRepo {

@TempDir
Path tempDir;

@BeforeEach
void setRepository() {
Helm.repo().add().withRepositoryConfig(tempDir.resolve("repositories.yaml"))
.withName("repo-1")
.withUrl(URI.create("https://charts.helm.sh/stable"))
.insecureSkipTlsVerify()
.call();
}

@Test
void withDefaults() {
final List<SearchResult> result = Helm.search().repo()
.withRepositoryConfig(tempDir.resolve("repositories.yaml"))
.call();
assertThat(result)
.isNotEmpty()
.first()
.extracting(SearchResult::getName, SearchResult::getScore, SearchResult::getChartVersion, SearchResult::getAppVersion, SearchResult::getDescription)
.allMatch(s -> s != null && !s.toString().isEmpty());
}

@Test
void withKeyword() {
final List<SearchResult> result = Helm.search().repo()
.withRepositoryConfig(tempDir.resolve("repositories.yaml"))
.withKeyword("nginx")
.call();
assertThat(result)
.isNotEmpty()
.allMatch(r -> r.getName().contains("nginx") || r.getDescription().contains("nginx") || r.getKeywords().contains("nginx"));
}

@Test
void hasNoDevelVersions() {
final List<SearchResult> result = Helm.search().repo()
.withRepositoryConfig(tempDir.resolve("repositories.yaml"))
.withKeyword("nginx")
.call();
assertThat(result)
.extracting(SearchResult::getChartVersion)
.noneMatch(chartVersion -> chartVersion.contains("-"));
}

@Test
void withDevelHasDevelVersions() {
final List<SearchResult> result = Helm.search().repo()
.withRepositoryConfig(tempDir.resolve("repositories.yaml"))
.devel()
.call();
assertThat(result)
.extracting(SearchResult::getChartVersion)
.anyMatch(chartVersion -> chartVersion.contains("-"));
}

}
}
2 changes: 2 additions & 0 deletions lib/api/src/main/java/com/marcnuri/helm/jni/HelmLib.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,8 @@ public interface HelmLib extends Library {

Result RepoServerStopAll();

Result SearchRepo(SearchOptions options);

Result Show(ShowOptions options);

Result Test(TestOptions options);
Expand Down
21 changes: 21 additions & 0 deletions lib/api/src/main/java/com/marcnuri/helm/jni/SearchOptions.java
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
package com.marcnuri.helm.jni;

import com.sun.jna.Structure;

@Structure.FieldOrder({"repositoryConfig", "keyword", "regexp", "devel", "version"})
public class SearchOptions extends Structure {

public String repositoryConfig;
public String keyword;
public int regexp;
public int devel;
public String version;

public SearchOptions(String repositoryConfig, String keyword, int regexp, int devel, String version) {
this.repositoryConfig = repositoryConfig;
this.keyword = keyword;
this.regexp = regexp;
this.devel = devel;
this.version = version;
}
}
6 changes: 4 additions & 2 deletions native/internal/helm/search.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@ import (
"net/url"
"path/filepath"
"strconv"
"strings"
)

type SearchOptions struct {
Expand DownExpand Up@@ -73,8 +74,9 @@ func SearchRepo(options *SearchOptions) (string, error) {
values.Set("name", searchResult.Name)
values.Set("score", strconv.Itoa(searchResult.Score))
values.Set("chartVersion", searchResult.Chart.Version)
values.Set("chartAppVersion", searchResult.Chart.AppVersion)
values.Set("chartDescription", searchResult.Chart.Description)
values.Set("appVersion", searchResult.Chart.AppVersion)
values.Set("description", searchResult.Chart.Description)
values.Set("keywords", strings.Join(searchResult.Chart.Metadata.Keywords, ","))
_, _ = fmt.Fprintln(out, values.Encode())
}
}
Expand Down
8 changes: 4 additions & 4 deletions native/main_test.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -565,12 +565,12 @@ func TestSearchRepo(t *testing.T) {
t.Errorf("Expected search to succeed, got %s", err)
return
}
if !strings.Contains(out, "chartAppVersion=") {
t.Errorf("Expected search to contain 'chartAppVersion=', got %s", out)
if !strings.Contains(out, "appVersion=") {
t.Errorf("Expected search to contain 'appVersion=', got %s", out)
return
}
if !strings.Contains(out, "&chartDescription=") {
t.Errorf("Expected search to contain '&chartDescription=', got %s", out)
if !strings.Contains(out, "&description=") {
t.Errorf("Expected search to contain '&description=', got %s", out)
return
}
if !strings.Contains(out, "&name=") {
Expand Down