Skip to content

Commit

Permalink
feat(artifacts): dynamic artifact credentials via kork-credentials (#…
Browse files Browse the repository at this point in the history
…5135)

* feat(artifacts): kork-credentials for artifacts

* feat(artifacts): remove unused dependencies
  • Loading branch information
ncknt committed Jan 15, 2021
1 parent 0c7280f commit c76ef93
Show file tree
Hide file tree
Showing 57 changed files with 509 additions and 370 deletions.
2 changes: 2 additions & 0 deletions clouddriver-artifacts/clouddriver-artifacts.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dependencies {

compileOnly "org.projectlombok:lombok"
annotationProcessor "org.projectlombok:lombok"
testAnnotationProcessor "org.projectlombok:lombok"

implementation "com.amazonaws:aws-java-sdk-s3"
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-xml"
Expand All @@ -15,6 +16,7 @@ dependencies {
implementation 'com.google.auth:google-auth-library-oauth2-http'
implementation "com.netflix.frigga:frigga"
implementation "com.netflix.spinnaker.kork:kork-artifacts"
implementation "com.netflix.spinnaker.kork:kork-credentials"
implementation "com.netflix.spinnaker.kork:kork-annotations"
implementation "com.netflix.spinnaker.kork:kork-exceptions"
implementation "com.netflix.spinnaker.kork:kork-security"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,55 +17,16 @@

package com.netflix.spinnaker.clouddriver.artifacts;

import com.google.common.base.Strings;
import com.netflix.spinnaker.clouddriver.artifacts.config.ArtifactCredentials;
import java.util.Collection;
import java.util.Collections;
import com.netflix.spinnaker.credentials.CompositeCredentialsRepository;
import com.netflix.spinnaker.credentials.CredentialsRepository;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.Getter;
import org.springframework.stereotype.Component;

@Component
public class ArtifactCredentialsRepository {
@Getter private final List<ArtifactCredentials> allCredentials;
public class ArtifactCredentialsRepository
extends CompositeCredentialsRepository<ArtifactCredentials> {

public ArtifactCredentialsRepository(List<List<? extends ArtifactCredentials>> allCredentials) {
this.allCredentials =
Collections.unmodifiableList(
allCredentials.stream()
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toList()));
}

private ArtifactCredentials getCredentials(String accountName) {
return getAllCredentials().stream()
.filter(e -> e.getName().equals(accountName))
.findFirst()
.orElseThrow(
() ->
new IllegalArgumentException(
"No credentials with name '" + accountName + "' could be found."));
}

public ArtifactCredentials getCredentials(String accountName, String type) {
if (Strings.isNullOrEmpty(accountName)) {
throw new IllegalArgumentException(
"An artifact account must be supplied to download this artifact: " + accountName);
}

ArtifactCredentials credentials = getCredentials(accountName);
if (!credentials.handlesType(type)) {
throw new IllegalArgumentException(
"Artifact credentials '"
+ accountName
+ "' cannot handle artifacts of type '"
+ type
+ "'");
}

return credentials;
public ArtifactCredentialsRepository(
List<CredentialsRepository<? extends ArtifactCredentials>> repositories) {
super(repositories);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
package com.netflix.spinnaker.clouddriver.artifacts;

import com.netflix.spinnaker.kork.artifacts.model.Artifact;
import com.netflix.spinnaker.kork.exceptions.MissingCredentialsException;
import com.netflix.spinnaker.kork.exceptions.UnknownCredentialsTypeException;
import com.netflix.spinnaker.kork.web.exceptions.InvalidRequestException;
import com.netflix.spinnaker.kork.web.exceptions.NotFoundException;
import java.io.IOException;
import java.io.InputStream;
import lombok.RequiredArgsConstructor;
Expand All @@ -29,8 +33,14 @@ public class ArtifactDownloader {
private final ArtifactCredentialsRepository artifactCredentialsRepository;

public InputStream download(Artifact artifact) throws IOException {
return artifactCredentialsRepository
.getCredentials(artifact.getArtifactAccount(), artifact.getType())
.download(artifact);
try {
return artifactCredentialsRepository
.getCredentials(artifact.getArtifactAccount(), artifact.getType())
.download(artifact);
} catch (UnknownCredentialsTypeException e) {
throw new InvalidRequestException(e);
} catch (MissingCredentialsException e) {
throw new NotFoundException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

@NonnullByDefault
@Value
final class BitbucketArtifactAccount implements ArtifactAccount, BasicAuth {
public class BitbucketArtifactAccount implements ArtifactAccount, BasicAuth {
private final String name;
private final Optional<String> username;
private final Optional<String> password;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@

package com.netflix.spinnaker.clouddriver.artifacts.bitbucket;

import com.netflix.spinnaker.credentials.CredentialsTypeProperties;
import com.squareup.okhttp.OkHttpClient;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
Expand All @@ -37,19 +35,15 @@ class BitbucketArtifactConfiguration {
private final BitbucketArtifactProviderProperties bitbucketArtifactProviderProperties;

@Bean
List<? extends BitbucketArtifactCredentials> bitbucketArtifactCredentials(
OkHttpClient okHttpClient) {
return bitbucketArtifactProviderProperties.getAccounts().stream()
.map(
a -> {
try {
return new BitbucketArtifactCredentials(a, okHttpClient);
} catch (Exception e) {
log.warn("Failure instantiating Bitbucket artifact account {}: ", a, e);
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
public CredentialsTypeProperties<BitbucketArtifactCredentials, BitbucketArtifactAccount>
bitbucketCredentialsProperties(OkHttpClient okHttpClient) {
return CredentialsTypeProperties
.<BitbucketArtifactCredentials, BitbucketArtifactAccount>builder()
.type(BitbucketArtifactCredentials.CREDENTIALS_TYPE)
.credentialsClass(BitbucketArtifactCredentials.class)
.credentialsDefinitionClass(BitbucketArtifactAccount.class)
.defaultCredentialsSource(bitbucketArtifactProviderProperties::getAccounts)
.credentialsParser(bc -> new BitbucketArtifactCredentials(bc, okHttpClient))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@

@NonnullByDefault
@Slf4j
final class BitbucketArtifactCredentials
public class BitbucketArtifactCredentials
extends SimpleHttpArtifactCredentials<BitbucketArtifactAccount> implements ArtifactCredentials {
public static final String CREDENTIALS_TYPE = "artifacts-bitbucket";
@Getter private final String name;
@Getter private final ImmutableList<String> types = ImmutableList.of("bitbucket/file");

BitbucketArtifactCredentials(BitbucketArtifactAccount account, OkHttpClient okHttpClient) {
super(okHttpClient, account);
this.name = account.getName();
}

@Override
public String getType() {
return CREDENTIALS_TYPE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package com.netflix.spinnaker.clouddriver.artifacts.config;

public interface ArtifactAccount {
import com.netflix.spinnaker.credentials.definition.CredentialsDefinition;

public interface ArtifactAccount extends CredentialsDefinition {
String getName();
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package com.netflix.spinnaker.clouddriver.artifacts.config;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.netflix.spinnaker.credentials.Credentials;
import com.netflix.spinnaker.kork.annotations.NonnullByDefault;
import com.netflix.spinnaker.kork.artifacts.model.Artifact;
import java.io.IOException;
Expand All @@ -27,7 +28,7 @@
import org.apache.commons.lang3.NotImplementedException;

@NonnullByDefault
public interface ArtifactCredentials {
public interface ArtifactCredentials extends Credentials {
String getName();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@

package com.netflix.spinnaker.clouddriver.artifacts.custom;

import java.util.Collections;
import java.util.List;
import com.netflix.spinnaker.credentials.CredentialsRepository;
import com.netflix.spinnaker.credentials.MapBackedCredentialsRepository;
import com.netflix.spinnaker.credentials.NoopCredentialsLifecycleHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
Expand All @@ -28,10 +29,13 @@
@RequiredArgsConstructor
@Slf4j
class CustomArtifactConfiguration {

@Bean
List<? extends CustomArtifactCredentials> customArtifactCredentials() {
CustomArtifactAccount account = new CustomArtifactAccount();
CustomArtifactCredentials credentials = new CustomArtifactCredentials(account);
return Collections.singletonList(credentials);
public CredentialsRepository<CustomArtifactCredentials> customArtifactCredentialsRepository() {
CredentialsRepository<CustomArtifactCredentials> repository =
new MapBackedCredentialsRepository<>(
CustomArtifactCredentials.CREDENTIALS_TYPE, new NoopCredentialsLifecycleHandler<>());
repository.save(new CustomArtifactCredentials(new CustomArtifactAccount()));
return repository;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
@NonnullByDefault
@Slf4j
final class CustomArtifactCredentials implements ArtifactCredentials {
public static final String CREDENTIALS_TYPE = "artifacts-custom";
@Getter private final String name;
@Getter private final ImmutableList<String> types = ImmutableList.of("custom/object");

Expand All @@ -39,4 +40,9 @@ public InputStream download(Artifact artifact) {
throw new UnsupportedOperationException(
"Custom references are passed on to cloud platforms to handle or process");
}

@Override
public String getType() {
return CREDENTIALS_TYPE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@

package com.netflix.spinnaker.clouddriver.artifacts.docker;

import java.util.Collections;
import java.util.List;
import com.netflix.spinnaker.credentials.CredentialsRepository;
import com.netflix.spinnaker.credentials.MapBackedCredentialsRepository;
import com.netflix.spinnaker.credentials.NoopCredentialsLifecycleHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
Expand All @@ -30,8 +31,13 @@
@RequiredArgsConstructor
@Slf4j
class DockerArtifactConfiguration {

@Bean
List<? extends DockerArtifactCredentials> dockerArtifactCredentials() {
return Collections.singletonList(new DockerArtifactCredentials(new DockerArtifactAccount()));
public CredentialsRepository<DockerArtifactCredentials> dockerArtifactCredentialsRepository() {
CredentialsRepository<DockerArtifactCredentials> repository =
new MapBackedCredentialsRepository<>(
DockerArtifactCredentials.CREDENTIALS_TYPE, new NoopCredentialsLifecycleHandler<>());
repository.save(new DockerArtifactCredentials(new DockerArtifactAccount()));
return repository;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
@NonnullByDefault
@Value
final class DockerArtifactCredentials implements ArtifactCredentials {
public static final String CREDENTIALS_TYPE = "artifacts-docker";
public static final String TYPE = "docker/image";

private final String name;
Expand All @@ -42,4 +43,9 @@ public InputStream download(Artifact artifact) {
throw new UnsupportedOperationException(
"Docker references are passed on to cloud platforms who retrieve images directly");
}

@Override
public String getType() {
return CREDENTIALS_TYPE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@

package com.netflix.spinnaker.clouddriver.artifacts.embedded;

import java.util.Collections;
import java.util.List;
import com.netflix.spinnaker.credentials.CredentialsRepository;
import com.netflix.spinnaker.credentials.MapBackedCredentialsRepository;
import com.netflix.spinnaker.credentials.NoopCredentialsLifecycleHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
Expand All @@ -29,9 +30,12 @@
@Slf4j
class EmbeddedArtifactConfiguration {
@Bean
List<? extends EmbeddedArtifactCredentials> embeddedArtifactCredentials() {
EmbeddedArtifactAccount account = new EmbeddedArtifactAccount();
EmbeddedArtifactCredentials credentials = new EmbeddedArtifactCredentials(account);
return Collections.singletonList(credentials);
public CredentialsRepository<EmbeddedArtifactCredentials>
embeddedArtifactCredentialsRepository() {
CredentialsRepository<EmbeddedArtifactCredentials> repository =
new MapBackedCredentialsRepository<>(
EmbeddedArtifactCredentials.CREDENTIALS_TYPE, new NoopCredentialsLifecycleHandler<>());
repository.save(new EmbeddedArtifactCredentials(new EmbeddedArtifactAccount()));
return repository;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
@NonnullByDefault
@Slf4j
final class EmbeddedArtifactCredentials implements ArtifactCredentials {
public static final String CREDENTIALS_TYPE = "artifacts-embedded";
@Getter private final String name;
@Getter private final ImmutableList<String> types = ImmutableList.of("embedded/base64");

Expand Down Expand Up @@ -60,4 +61,9 @@ private InputStream fromBase64(Artifact artifact) {
public boolean handlesType(String type) {
return type.startsWith("embedded/");
}

@Override
public String getType() {
return CREDENTIALS_TYPE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.spinnaker.clouddriver.core.services.Front50Service;
import java.util.Collections;
import java.util.List;
import com.netflix.spinnaker.credentials.CredentialsRepository;
import com.netflix.spinnaker.credentials.MapBackedCredentialsRepository;
import com.netflix.spinnaker.credentials.NoopCredentialsLifecycleHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
Expand All @@ -30,9 +31,12 @@
@Slf4j
class Front50ArtifactConfiguration {
@Bean
List<? extends Front50ArtifactCredentials> front50ArtifactCredentials(
public CredentialsRepository<Front50ArtifactCredentials> front50ArtifactCredentialsRepository(
ObjectMapper objectMapper, Front50Service front50Service) {
Front50ArtifactCredentials c = new Front50ArtifactCredentials(objectMapper, front50Service);
return Collections.singletonList(c);
CredentialsRepository<Front50ArtifactCredentials> repository =
new MapBackedCredentialsRepository<>(
Front50ArtifactCredentials.CREDENTIALS_TYPE, new NoopCredentialsLifecycleHandler<>());
repository.save(new Front50ArtifactCredentials(objectMapper, front50Service));
return repository;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
@NonnullByDefault
@Slf4j
final class Front50ArtifactCredentials implements ArtifactCredentials {
public static final String CREDENTIALS_TYPE = "artifacts-front50";
private static final String ACCOUNT_NAME = "front50ArtifactCredentials";
private static final String URL_PREFIX = "spinnaker://";

Expand Down Expand Up @@ -106,6 +107,11 @@ private SplitResult splitReferenceOnToken(String reference, String token) {
return new SplitResult(refParts[0], refParts[1]);
}

@Override
public String getType() {
return CREDENTIALS_TYPE;
}

@Data
@AllArgsConstructor
private static class SplitResult {
Expand Down
Loading

0 comments on commit c76ef93

Please sign in to comment.