Skip to content

Commit

Permalink
Merge pull request #22 from sheroy/master
Browse files Browse the repository at this point in the history
Sets an environment variable with artifact image details
  • Loading branch information
bdpiprava committed Nov 28, 2018
2 parents 7862ab7 + 1c6595f commit 65a0ebe
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 21 deletions.
Expand Up @@ -22,7 +22,7 @@
import com.thoughtworks.go.plugin.api.response.DefaultGoApiResponse;
import com.thoughtworks.go.plugin.api.response.GoApiResponse;

import static cd.go.artifact.docker.registry.Constants.API_VERSION;
import static cd.go.artifact.docker.registry.Constants.CONSOLE_LOG_PROCESSOR_API_VERSION;
import static cd.go.artifact.docker.registry.Constants.PLUGIN_IDENTIFIER;
import static cd.go.artifact.docker.registry.DockerRegistryArtifactPlugin.LOG;

Expand All @@ -43,7 +43,7 @@ public void error(String message) {
}

private void sendLog(ConsoleLogMessage consoleLogMessage) {
DefaultGoApiRequest request = new DefaultGoApiRequest(Constants.SEND_CONSOLE_LOG, API_VERSION, PLUGIN_IDENTIFIER);
DefaultGoApiRequest request = new DefaultGoApiRequest(Constants.SEND_CONSOLE_LOG, CONSOLE_LOG_PROCESSOR_API_VERSION, PLUGIN_IDENTIFIER);
request.setRequestBody(consoleLogMessage.toJSON());

GoApiResponse response = accessor.submit(request);
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/cd/go/artifact/docker/registry/Constants.java
Expand Up @@ -25,10 +25,11 @@ public interface Constants {
String EXTENSION_TYPE = "artifact";

// The extension point API version that this plugin understands
String API_VERSION = "1.0";
String EXTENSION_API_VERSION = "2.0";
String CONSOLE_LOG_PROCESSOR_API_VERSION = "1.0";

// the identifier of this plugin
GoPluginIdentifier PLUGIN_IDENTIFIER = new GoPluginIdentifier(EXTENSION_TYPE, Collections.singletonList(API_VERSION));
GoPluginIdentifier PLUGIN_IDENTIFIER = new GoPluginIdentifier(EXTENSION_TYPE, Collections.singletonList(EXTENSION_API_VERSION));

// requests that the plugin makes to the server
String REQUEST_SERVER_PREFIX = "go.processor";
Expand Down
Expand Up @@ -74,7 +74,7 @@ public GoPluginApiResponse handle(GoPluginApiRequest request) {
case REQUEST_FETCH_ARTIFACT_VIEW:
return new GetFetchArtifactViewExecutor().execute();
case REQUEST_FETCH_ARTIFACT_VALIDATE:
return new ValidateFetchArtifactConfigExecutor().execute();
return new ValidateFetchArtifactConfigExecutor(request).execute();
case REQUEST_PUBLISH_ARTIFACT:
return new PublishArtifactExecutor(request, consoleLogger).execute();
case REQUEST_FETCH_ARTIFACT:
Expand Down
Expand Up @@ -20,13 +20,17 @@
import cd.go.artifact.docker.registry.DockerClientFactory;
import cd.go.artifact.docker.registry.DockerProgressHandler;
import cd.go.artifact.docker.registry.model.ArtifactStoreConfig;
import cd.go.artifact.docker.registry.model.FetchArtifactConfig;
import cd.go.artifact.docker.registry.utils.Util;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.spotify.docker.client.DockerClient;
import com.thoughtworks.go.plugin.api.request.GoPluginApiRequest;
import com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse;
import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse;
import org.apache.commons.lang.StringUtils;

import java.util.Map;

Expand Down Expand Up @@ -55,7 +59,9 @@ public GoPluginApiResponse execute() {
try {
final Map<String, String> artifactMap = fetchArtifactRequest.getMetadata();
validateMetadata(artifactMap);
FetchArtifactConfig fetchArtifactConfig = fetchArtifactRequest.getFetchArtifactConfig();

final String artifactPrefix = fetchArtifactConfig.getEnvironmentVariablePrefix();
final String imageToPull = artifactMap.get("image");

consoleLogger.info(String.format("Pulling docker image `%s` from docker registry `%s`.", imageToPull, fetchArtifactRequest.getArtifactStoreConfig().getRegistryUrl()));
Expand All @@ -71,7 +77,14 @@ public GoPluginApiResponse execute() {
throw new RuntimeException(format("Expecting pulled image digest to be [%s] but it is [%s].", artifactMap.get("digest"), dockerProgressHandler.getDigest()));
}

return DefaultGoPluginApiResponse.success("");
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("name", StringUtils.isEmpty(artifactPrefix) ? "ARTIFACT_IMAGE" : String.format("%s_ARTIFACT_IMAGE", artifactPrefix));
jsonObject.addProperty("value", imageToPull);

JsonArray jsonElements = new JsonArray();
jsonElements.add(jsonObject);

return DefaultGoPluginApiResponse.success(jsonElements.toString());
} catch (Exception e) {
final String message = format("Failed pull docker image: %s", e);
consoleLogger.error(message);
Expand All @@ -95,19 +108,26 @@ public void validateMetadata(Map<String, String> artifactMap) {
}

protected static class FetchArtifactRequest {

@Expose
@SerializedName("fetch_artifact_configuration")
private FetchArtifactConfig fetchArtifactConfig;

@Expose
@SerializedName("store_configuration")
private ArtifactStoreConfig artifactStoreConfig;

@Expose
@SerializedName("artifact_metadata")
private Map<String, String> metadata;

public FetchArtifactRequest() {
}

public FetchArtifactRequest(ArtifactStoreConfig artifactStoreConfig, Map<String, String> metadata) {
public FetchArtifactRequest(ArtifactStoreConfig artifactStoreConfig, Map<String, String> metadata, FetchArtifactConfig fetchArtifactConfig) {
this.artifactStoreConfig = artifactStoreConfig;
this.metadata = metadata;
this.fetchArtifactConfig = fetchArtifactConfig;
}

public ArtifactStoreConfig getArtifactStoreConfig() {
Expand All @@ -121,5 +141,9 @@ public Map<String, String> getMetadata() {
public static FetchArtifactRequest fromJSON(String json) {
return Util.GSON.fromJson(json, FetchArtifactRequest.class);
}

public FetchArtifactConfig getFetchArtifactConfig() {
return fetchArtifactConfig;
}
}
}
Expand Up @@ -16,15 +16,23 @@

package cd.go.artifact.docker.registry.executors;

import cd.go.artifact.docker.registry.annotation.ConfigMetadata;
import cd.go.artifact.docker.registry.annotation.MetadataHelper;
import cd.go.artifact.docker.registry.model.BuildFileArtifactPlanConfig;
import cd.go.artifact.docker.registry.model.FetchArtifactConfig;
import cd.go.artifact.docker.registry.model.ImageTagArtifactPlanConfig;
import cd.go.artifact.docker.registry.utils.Util;
import com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse;
import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse;

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

public class GetFetchArtifactMetadataExecutor implements RequestExecutor {
public GoPluginApiResponse execute() {
return DefaultGoPluginApiResponse.success(Util.GSON.toJson(new ArrayList<>()));

final List<ConfigMetadata> metadata = MetadataHelper.getMetadata(FetchArtifactConfig.class);
return DefaultGoPluginApiResponse.success(Util.GSON.toJson(metadata));
}
}

Expand Up @@ -17,14 +17,21 @@
package cd.go.artifact.docker.registry.executors;

import cd.go.artifact.docker.registry.annotation.ValidationResult;
import cd.go.artifact.docker.registry.model.FetchArtifactConfig;
import com.thoughtworks.go.plugin.api.request.GoPluginApiRequest;
import com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse;
import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse;

public class ValidateFetchArtifactConfigExecutor implements RequestExecutor {

private final FetchArtifactConfig fetchArtifactConfig;

public ValidateFetchArtifactConfigExecutor(GoPluginApiRequest request) {
this.fetchArtifactConfig = FetchArtifactConfig.fromJSON(request.requestBody());
}

@Override
public GoPluginApiResponse execute() {
final ValidationResult validationResult = new ValidationResult();
return DefaultGoPluginApiResponse.success(validationResult.toJSON());
return DefaultGoPluginApiResponse.success(fetchArtifactConfig.validate().toJSON());
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2018 ThoughtWorks, 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 cd.go.artifact.docker.registry.model;

import cd.go.artifact.docker.registry.annotation.FieldMetadata;
import cd.go.artifact.docker.registry.annotation.Validatable;
import cd.go.artifact.docker.registry.annotation.ValidationError;
import cd.go.artifact.docker.registry.annotation.ValidationResult;
import cd.go.artifact.docker.registry.utils.Util;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import org.apache.commons.lang.StringUtils;

public class FetchArtifactConfig implements Validatable {
@Expose
@SerializedName("EnvironmentVariablePrefix")
@FieldMetadata(key = "EnvironmentVariablePrefix", required = false)
private String environmentVariablePrefix;

public FetchArtifactConfig() {
}

public FetchArtifactConfig(String environmentVariablePrefix) {
this.environmentVariablePrefix = environmentVariablePrefix;
}

public static FetchArtifactConfig fromJSON(String json) {
return Util.GSON.fromJson(json, FetchArtifactConfig.class);
}

public String getEnvironmentVariablePrefix() {
return environmentVariablePrefix;
}

@Override
public ValidationResult validate() {
ValidationResult validationResult = new ValidationResult();
if (StringUtils.isNotBlank(environmentVariablePrefix) && !environmentVariablePrefix.matches("(?i)[a-z][a-z0-9_]*")) {
validationResult.addError(new ValidationError("EnvironmentVariablePrefix", "Invalid environment name prefix. Valid prefixes contain characters, numbers, and underscore; and can't start with a number."));
}
return validationResult;
}
}
6 changes: 5 additions & 1 deletion src/main/resources/fetch-artifact.template.html
@@ -1 +1,5 @@
<div></div>
<div class="form_item_block">
<label ng-class="{'is-invalid-label': GOINPUTNAME[EnvironmentVariablePrefix].$error.server}">Environment Variable Prefix:</label>
<input ng-class="{'is-invalid-input': GOINPUTNAME[EnvironmentVariablePrefix].$error.server}" type="text" ng-model="EnvironmentVariablePrefix" placeholder="PREFIX"/>
<span class="form_error form-error" ng-class="{'is-visible': GOINPUTNAME[EnvironmentVariablePrefix].$error.server}" ng-show="GOINPUTNAME[EnvironmentVariablePrefix].$error.server">{{GOINPUTNAME[EnvironmentVariablePrefix].$error.server}}</span>
</div>
Expand Up @@ -50,7 +50,7 @@ public void shouldLogInfoMessageToConsoleLog() throws JSONException {

final GoApiRequest request = argumentCaptor.getValue();
assertThat(request.api()).isEqualTo(Constants.SEND_CONSOLE_LOG);
assertThat(request.apiVersion()).isEqualTo(Constants.API_VERSION);
assertThat(request.apiVersion()).isEqualTo(Constants.CONSOLE_LOG_PROCESSOR_API_VERSION);

final String expectedJSON = "{\n" +
" \"logLevel\": \"INFO\",\n" +
Expand All @@ -66,7 +66,7 @@ public void shouldLogErrorMessageToConsoleLog() throws JSONException {

final GoApiRequest request = argumentCaptor.getValue();
assertThat(request.api()).isEqualTo(Constants.SEND_CONSOLE_LOG);
assertThat(request.apiVersion()).isEqualTo(Constants.API_VERSION);
assertThat(request.apiVersion()).isEqualTo(Constants.CONSOLE_LOG_PROCESSOR_API_VERSION);

final String expectedJSON = "{\n" +
" \"logLevel\": \"ERROR\",\n" +
Expand Down
Expand Up @@ -20,6 +20,7 @@
import cd.go.artifact.docker.registry.DockerClientFactory;
import cd.go.artifact.docker.registry.DockerProgressHandler;
import cd.go.artifact.docker.registry.model.ArtifactStoreConfig;
import cd.go.artifact.docker.registry.model.FetchArtifactConfig;
import com.google.gson.Gson;
import com.spotify.docker.client.DefaultDockerClient;
import com.spotify.docker.client.exceptions.DockerCertificateException;
Expand Down Expand Up @@ -69,24 +70,45 @@ public void shouldFetchArtifact() {
final HashMap<String, String> artifactMetadata = new HashMap<>();
artifactMetadata.put("image", "localhost:5000/alpine:v1");
artifactMetadata.put("digest", "foo");
final FetchArtifactRequest fetchArtifactRequest = new FetchArtifactRequest(storeConfig, artifactMetadata);

FetchArtifactConfig fetchArtifactConfig = new FetchArtifactConfig("PREFIX");

final FetchArtifactRequest fetchArtifactRequest = new FetchArtifactRequest(storeConfig, artifactMetadata, fetchArtifactConfig);

when(request.requestBody()).thenReturn(new Gson().toJson(fetchArtifactRequest));
when(dockerProgressHandler.getDigest()).thenReturn("foo");

final GoPluginApiResponse response = new FetchArtifactExecutor(request, consoleLogger, dockerProgressHandler, dockerClientFactory).execute();

assertThat(response.responseCode()).isEqualTo(200);
assertThat(response.responseBody()).isEqualTo("");
assertThat(response.responseBody()).isEqualTo("[{\"name\":\"PREFIX_ARTIFACT_IMAGE\",\"value\":\"localhost:5000/alpine:v1\"}]");
}

@Test
public void shouldSetEnvironmentVariablesWithImageInformationInResponseRegardlessOfWhetherThePrefixIsProvided() {
final ArtifactStoreConfig storeConfig = new ArtifactStoreConfig("localhost:5000", "admin", "admin123");
final HashMap<String, String> artifactMetadata = new HashMap<>();
artifactMetadata.put("image", "localhost:5000/alpine:v1");
artifactMetadata.put("digest", "foo");
final FetchArtifactRequest fetchArtifactRequest = new FetchArtifactRequest(storeConfig, artifactMetadata, new FetchArtifactConfig());

when(request.requestBody()).thenReturn(new Gson().toJson(fetchArtifactRequest));
when(dockerProgressHandler.getDigest()).thenReturn("foo");

final GoPluginApiResponse response = new FetchArtifactExecutor(request, consoleLogger, dockerProgressHandler, dockerClientFactory).execute();

assertThat(response.responseCode()).isEqualTo(200);
assertThat(response.responseBody()).isEqualTo("[{\"name\":\"ARTIFACT_IMAGE\",\"value\":\"localhost:5000/alpine:v1\"}]");
}


@Test
public void shouldErrorOutWhenDigestIsNotSame() {
final ArtifactStoreConfig storeConfig = new ArtifactStoreConfig("localhost:5000", "admin", "admin123");
final HashMap<String, String> artifactMetadata = new HashMap<>();
artifactMetadata.put("image", "localhost:5000/alpine:v1");
artifactMetadata.put("digest", "foo");
final FetchArtifactRequest fetchArtifactRequest = new FetchArtifactRequest(storeConfig, artifactMetadata);
final FetchArtifactRequest fetchArtifactRequest = new FetchArtifactRequest(storeConfig, artifactMetadata, new FetchArtifactConfig());

when(request.requestBody()).thenReturn(new Gson().toJson(fetchArtifactRequest));
when(dockerProgressHandler.getDigest()).thenReturn("bar");
Expand All @@ -103,7 +125,7 @@ public void shouldErrorOutWhenFailedToPull() throws DockerException, Interrupted
final HashMap<String, String> artifactMetadata = new HashMap<>();
artifactMetadata.put("image", "localhost:5000/alpine:v1");
artifactMetadata.put("digest", "foo");
final FetchArtifactRequest fetchArtifactRequest = new FetchArtifactRequest(storeConfig, artifactMetadata);
final FetchArtifactRequest fetchArtifactRequest = new FetchArtifactRequest(storeConfig, artifactMetadata, new FetchArtifactConfig());

when(request.requestBody()).thenReturn(new Gson().toJson(fetchArtifactRequest));
doThrow(new RuntimeException("Some error")).when(dockerClient).pull("localhost:5000/alpine:v1", dockerProgressHandler);
Expand Down
Expand Up @@ -27,8 +27,7 @@ public class GetFetchArtifactMetadataExecutorTest {
@Test
public void shouldReturnFetchArtifactMetadata() throws JSONException {
final GoPluginApiResponse response = new GetFetchArtifactMetadataExecutor().execute();

final String expectedJSON = "[]";
final String expectedJSON = "[{\"key\":\"EnvironmentVariablePrefix\",\"metadata\":{\"required\":false,\"secure\":false}}]";

assertThat(response.responseCode()).isEqualTo(200);
JSONAssert.assertEquals(expectedJSON, response.responseBody(), true);
Expand Down

0 comments on commit 65a0ebe

Please sign in to comment.