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(cloudfoundry): support docker deployments #5208

Merged
merged 4 commits into from
Feb 3, 2021
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 @@ -21,12 +21,12 @@
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty("kubernetes.enabled")
@ConditionalOnExpression("${kubernetes.enabled:false} || ${dockerRegistry.enabled:false}")
@RequiredArgsConstructor
@Slf4j
class DockerArtifactConfiguration {
Expand Down
1 change: 1 addition & 0 deletions clouddriver-cloudfoundry/clouddriver-cloudfoundry.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation project(":clouddriver-core")
implementation project(":clouddriver-security")
implementation project(":cats:cats-core")
implementation project(":clouddriver-docker")

compileOnly "org.projectlombok:lombok"
annotationProcessor "org.projectlombok:lombok"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.*;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.Package;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.Process;
import com.netflix.spinnaker.clouddriver.cloudfoundry.deploy.description.DeployCloudFoundryServerGroupDescription;
import com.netflix.spinnaker.clouddriver.cloudfoundry.model.*;
import com.netflix.spinnaker.clouddriver.helpers.AbstractServerGroupNameResolver;
import com.netflix.spinnaker.clouddriver.model.HealthState;
Expand Down Expand Up @@ -536,17 +535,15 @@ public void deleteAppInstance(String guid, String index) throws CloudFoundryApiE
public CloudFoundryServerGroup createApplication(
String appName,
CloudFoundrySpace space,
DeployCloudFoundryServerGroupDescription.ApplicationAttributes applicationAttributes,
@Nullable Map<String, String> environmentVariables)
@Nullable Map<String, String> environmentVariables,
Lifecycle lifecycle)
throws CloudFoundryApiException {
Map<String, ToOneRelationship> relationships = new HashMap<>();
relationships.put("space", new ToOneRelationship(new Relationship(space.getId())));

return safelyCall(
() ->
api.createApplication(
new CreateApplication(
appName, relationships, environmentVariables, applicationAttributes)))
new CreateApplication(appName, relationships, environmentVariables, lifecycle)))
.map(this::map)
.orElseThrow(
() ->
Expand Down Expand Up @@ -589,8 +586,8 @@ public void updateProcess(
safelyCall(() -> api.updateProcess(guid, new UpdateProcess(command, healthCheck)));
}

public String createPackage(String appGuid) throws CloudFoundryApiException {
return safelyCall(() -> api.createPackage(new CreatePackage(appGuid)))
public String createPackage(CreatePackage createPackageRequest) throws CloudFoundryApiException {
return safelyCall(() -> api.createPackage(createPackageRequest))
.map(Package::getGuid)
.orElseThrow(
() ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@
package com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.google.common.collect.ImmutableMap;
import com.netflix.spinnaker.clouddriver.cloudfoundry.deploy.description.DeployCloudFoundryServerGroupDescription;
import java.util.Map;
import javax.annotation.Nullable;
import lombok.AllArgsConstructor;
import lombok.Getter;

@JsonInclude(JsonInclude.Include.NON_NULL)
Expand All @@ -32,42 +29,16 @@ public class CreateApplication {

@Nullable private final Map<String, String> environmentVariables;

@Nullable private final BuildpackLifecycle lifecycle;
@Nullable private final Lifecycle lifecycle;

public CreateApplication(
String name,
Map<String, ToOneRelationship> relationships,
@Nullable Map<String, String> environmentVariables,
DeployCloudFoundryServerGroupDescription.ApplicationAttributes applicationAttributes) {
Lifecycle lifecycle) {
this.name = name;
this.relationships = relationships;
this.environmentVariables = environmentVariables;
this.lifecycle =
applicationAttributes.getBuildpacks() != null || applicationAttributes.getStack() != null
? new BuildpackLifecycle(applicationAttributes)
: null;
}

@AllArgsConstructor
@Getter
public static class BuildpackLifecycle {
private String type = "buildpack";
private Map<String, Object> data;

BuildpackLifecycle(
DeployCloudFoundryServerGroupDescription.ApplicationAttributes applicationAttributes) {
this.data =
new BuildpackLifecycleBuilder<String, Object>()
.putIfValueNotNull("buildpacks", applicationAttributes.getBuildpacks())
.putIfValueNotNull("stack", applicationAttributes.getStack())
.build();
}
}

static class BuildpackLifecycleBuilder<K, V> extends ImmutableMap.Builder<K, V> {
public BuildpackLifecycleBuilder<K, V> putIfValueNotNull(K key, V value) {
if (value != null) super.put(key, value);
return this;
}
this.lifecycle = lifecycle;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,27 @@

import java.util.HashMap;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;

@Getter
@Data
public class CreatePackage {
private final String type = "bits";
private final String type;
private final Map<String, ToOneRelationship> relationships = new HashMap<>();
private Docker data;

public CreatePackage(String appId) {
public CreatePackage(String appId, Type type, Docker data) {
this.type = type.getValue();
relationships.put("app", new ToOneRelationship(new Relationship(appId)));
this.data = data;
}

@Getter
@AllArgsConstructor
public enum Type {
BITS("bits"),
DOCKER("docker");
private String value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2021 Armory, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class Docker {
private final String image;
private final String username;
private final String password;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2021 Armory, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3;

import com.google.common.collect.ImmutableMap;
import com.netflix.spinnaker.clouddriver.cloudfoundry.deploy.description.DeployCloudFoundryServerGroupDescription;
import java.util.Collections;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
public class Lifecycle {
private final String type;
private final Map<String, Object> data;

public Lifecycle(
Type type,
DeployCloudFoundryServerGroupDescription.ApplicationAttributes applicationAttributes) {
this.type = type.getValue();
this.data =
type.equals(Type.BUILDPACK)
? new BuildpackLifecycleBuilder<String, Object>()
.putIfValueNotNull("buildpacks", applicationAttributes.getBuildpacks())
.putIfValueNotNull("stack", applicationAttributes.getStack())
.build()
: Collections.emptyMap();
}

@Getter
@AllArgsConstructor
public enum Type {
BUILDPACK("buildpack"),
DOCKER("docker");
private String value;
}

static class BuildpackLifecycleBuilder<K, V> extends ImmutableMap.Builder<K, V> {
public BuildpackLifecycleBuilder<K, V> putIfValueNotNull(K key, V value) {
if (value != null) super.put(key, value);
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@

import com.netflix.spinnaker.clouddriver.artifacts.ArtifactCredentialsRepository;
import com.netflix.spinnaker.clouddriver.cloudfoundry.CloudFoundryOperation;
import com.netflix.spinnaker.clouddriver.docker.registry.security.DockerRegistryNamedAccountCredentials;
import com.netflix.spinnaker.clouddriver.helpers.OperationPoller;
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperations;
import java.util.List;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

Expand All @@ -29,7 +31,8 @@ public class CloneCloudFoundryServerGroupAtomicOperationConverter
extends DeployCloudFoundryServerGroupAtomicOperationConverter {
public CloneCloudFoundryServerGroupAtomicOperationConverter(
@Qualifier("cloudFoundryOperationPoller") OperationPoller operationPoller,
ArtifactCredentialsRepository credentialsRepository) {
super(operationPoller, credentialsRepository);
ArtifactCredentialsRepository credentialsRepository,
List<? extends DockerRegistryNamedAccountCredentials> dockerRegistryNamedAccountCredentials) {
super(operationPoller, credentialsRepository, dockerRegistryNamedAccountCredentials);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
import com.netflix.spinnaker.clouddriver.artifacts.config.ArtifactCredentials;
import com.netflix.spinnaker.clouddriver.cloudfoundry.CloudFoundryOperation;
import com.netflix.spinnaker.clouddriver.cloudfoundry.artifacts.CloudFoundryArtifactCredentials;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.Docker;
import com.netflix.spinnaker.clouddriver.cloudfoundry.deploy.description.DeployCloudFoundryServerGroupDescription;
import com.netflix.spinnaker.clouddriver.cloudfoundry.deploy.ops.DeployCloudFoundryServerGroupAtomicOperation;
import com.netflix.spinnaker.clouddriver.cloudfoundry.security.CloudFoundryCredentials;
import com.netflix.spinnaker.clouddriver.docker.registry.security.DockerRegistryNamedAccountCredentials;
import com.netflix.spinnaker.clouddriver.helpers.OperationPoller;
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperation;
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperations;
Expand All @@ -49,12 +51,16 @@ public class DeployCloudFoundryServerGroupAtomicOperationConverter
extends AbstractCloudFoundryServerGroupAtomicOperationConverter {
private final OperationPoller operationPoller;
private final ArtifactCredentialsRepository credentialsRepository;
private final List<? extends DockerRegistryNamedAccountCredentials>
dockerRegistryNamedAccountCredentials;

public DeployCloudFoundryServerGroupAtomicOperationConverter(
@Qualifier("cloudFoundryOperationPoller") OperationPoller operationPoller,
ArtifactCredentialsRepository credentialsRepository) {
ArtifactCredentialsRepository credentialsRepository,
List<? extends DockerRegistryNamedAccountCredentials> dockerRegistryNamedAccountCredentials) {
this.operationPoller = operationPoller;
this.credentialsRepository = credentialsRepository;
this.dockerRegistryNamedAccountCredentials = dockerRegistryNamedAccountCredentials;
}

@Override
Expand Down Expand Up @@ -86,9 +92,32 @@ public DeployCloudFoundryServerGroupDescription convertDescription(Map input) {
converted.setApplicationAttributes(
convertManifest(
converted.getManifest().stream().findFirst().orElse(Collections.emptyMap())));
converted.setDocker(
converted.getArtifactCredentials().getTypes().contains("docker/image")
? resolveDockerAccount(converted.getApplicationArtifact())
: null);
return converted;
}

private Docker resolveDockerAccount(Artifact artifact) {
DockerRegistryNamedAccountCredentials dockerCreds =
dockerRegistryNamedAccountCredentials.stream()
.filter(reg -> reg.getRegistry().equals(artifact.getReference().split("/")[0]))
.filter(reg -> reg.getRepositories().contains(artifact.getName().split("/", 2)[1]))
.findFirst()
.orElseThrow(
() ->
new IllegalArgumentException(
"Could not find a docker registry for the docker image: "
+ artifact.getName()));

return Docker.builder()
.image(artifact.getReference())
.username(dockerCreds.getUsername())
.password(dockerCreds.getPassword())
.build();
}

private ArtifactCredentials getArtifactCredentials(
DeployCloudFoundryServerGroupDescription converted) {
Artifact artifact = converted.getApplicationArtifact();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.netflix.spinnaker.clouddriver.artifacts.config.ArtifactCredentials;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.Docker;
import com.netflix.spinnaker.clouddriver.cloudfoundry.model.CloudFoundrySpace;
import com.netflix.spinnaker.kork.artifacts.model.Artifact;
import java.util.List;
Expand Down Expand Up @@ -45,6 +46,8 @@ public class DeployCloudFoundryServerGroupDescription

@JsonIgnore private ApplicationAttributes applicationAttributes;

@JsonIgnore private Docker docker;

@Data
public static class ApplicationAttributes {
private int instances;
Expand Down
Loading