Skip to content

Commit

Permalink
feat(artifacts): add gitlab artifact provider (spinnaker#2630)
Browse files Browse the repository at this point in the history
adds a gitlab artifact provider. follows most of the same logic in the
github provider. relies on the gitlab raw file api.
  • Loading branch information
ethanfrogers authored May 15, 2018
1 parent 65fd423 commit 686e8d6
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2018 Armory
*
* 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.artifacts.gitlab;

import com.netflix.spinnaker.clouddriver.artifacts.config.ArtifactAccount;
import lombok.Data;
import lombok.EqualsAndHashCode;

@EqualsAndHashCode(callSuper = true)
@Data
public class GitlabArtifactAccount extends ArtifactAccount {
private String name;
private String token;
private String tokenFile;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2018 Armory
*
* 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.artifacts.gitlab;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.spinnaker.clouddriver.artifacts.ArtifactCredentialsRepository;
import com.squareup.okhttp.OkHttpClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.EnableScheduling;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Configuration
@ConditionalOnProperty("artifacts.gitlab.enabled")
@EnableScheduling
@Slf4j
public class GitlabArtifactConfiguration {
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
@ConfigurationProperties("artifacts.gitlab")
GitlabArtifactProviderProperties gitlabArtifactProviderProperties() {
return new GitlabArtifactProviderProperties();
}

@Autowired
GitlabArtifactProviderProperties gitlabArtifactProviderProperties;

@Autowired
ArtifactCredentialsRepository artifactCredentialsRepository;

@Autowired
OkHttpClient gitlabOkHttpClient;

@Autowired
ObjectMapper objectMapper;

@Bean
OkHttpClient gitlabOkHttpClient() {
return new OkHttpClient();
}

@Bean
List<? extends GitlabArtifactCredentials> gitlabArtifactCredentials() {
return gitlabArtifactProviderProperties.getAccounts()
.stream()
.map(a -> {
try {
GitlabArtifactCredentials c = new GitlabArtifactCredentials(a, gitlabOkHttpClient, objectMapper);
artifactCredentialsRepository.save(c);
return c;
} catch (Exception e) {
log.warn("Failure instantiating Gitlab artifact account {}: ", a, e);
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright 2018 Armory
*
* 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.artifacts.gitlab;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.spinnaker.clouddriver.artifacts.config.ArtifactCredentials;
import com.netflix.spinnaker.kork.artifacts.model.Artifact;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Request.Builder;
import com.squareup.okhttp.Response;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

@Slf4j
@Data
public class GitlabArtifactCredentials implements ArtifactCredentials {
private final String name;

@JsonIgnore
private final Builder requestBuilder;

@JsonIgnore
OkHttpClient okHttpClient;

@JsonIgnore
ObjectMapper objectMapper;

public GitlabArtifactCredentials(GitlabArtifactAccount account, OkHttpClient okHttpClient, ObjectMapper objectMapper) {
this.name = account.getName();
this.okHttpClient = okHttpClient;
this.objectMapper = objectMapper;
Builder builder = new Request.Builder();
boolean useToken = !StringUtils.isEmpty(account.getToken());
boolean useTokenFile = !StringUtils.isEmpty(account.getTokenFile());
boolean useAuth = useToken || useTokenFile;
if (useAuth) {
String authHeader = "";
if (useTokenFile) {
authHeader = credentialsFromFile(account.getTokenFile());
} else if (useToken) {
authHeader = account.getToken();
}

builder.header("Private-Token", authHeader);
log.info("Loaded credentials for Gitlab Artifact Account {}", account.getName());
} else {
log.info("No credentials included with Gitlab Artifact Account {}", account.getName());
}
requestBuilder = builder;
}

private String credentialsFromFile(String filename) {
try {
String credentials = FileUtils.readFileToString(new File(filename));
return credentials.replace("\n", "");
} catch (IOException e) {
log.error("Could not read Gitlab credentials file {}", filename);
return null;
}
}

public InputStream download(Artifact artifact) throws IOException {
HttpUrl.Builder fileUrl;
try {
// reference should use the Gitlab raw file download url: https://docs.gitlab.com/ee/api/repository_files.html#get-raw-file-from-repository
fileUrl = HttpUrl.parse(artifact.getReference()).newBuilder();
} catch (Exception e) {
throw new IllegalArgumentException("Malformed gitlab content URL in 'reference'. Read more here https://www.spinnaker.io/reference/artifacts/types/gitlab-file/: " + e.getMessage(), e);
}

String version = artifact.getVersion();
if (StringUtils.isEmpty(version)) {
log.info("No version specified for artifact {}, using 'master'.", version);
version = "master";
}

fileUrl.addQueryParameter("ref", version);
Request fileRequest = requestBuilder
.url(fileUrl.build().toString())
.build();

try {
Response downloadResponse = okHttpClient.newCall(fileRequest).execute();
return downloadResponse.body().byteStream();
} catch (IOException e) {
throw new com.netflix.spinnaker.clouddriver.artifacts.gitlab.GitlabArtifactCredentials.FailedDownloadException("Unable to download the contents of artifact " + artifact + ": " + e.getMessage(), e);
}
}

@Override
public boolean handlesType(String type) {
return type.equals("gitlab/file");
}

public class FailedDownloadException extends IOException {

public FailedDownloadException(String message, Throwable cause) {
super(message, cause);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2018 Netflix, 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.artifacts.gitlab;

import com.netflix.spinnaker.clouddriver.artifacts.config.ArtifactProvider;
import lombok.Data;

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

@Data
public class GitlabArtifactProviderProperties extends ArtifactProvider<GitlabArtifactAccount> {
private boolean enabled;
private List<GitlabArtifactAccount> accounts = new ArrayList<>();
}

0 comments on commit 686e8d6

Please sign in to comment.