Skip to content

Commit

Permalink
feat(artifacts): Add support for HTTP artifacts (spinnaker#2395)
Browse files Browse the repository at this point in the history
Basic auth is supported just like for GitHub artifact accounts.
  • Loading branch information
wjoel authored and lwander committed Feb 27, 2018
1 parent 188434e commit 4753361
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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;

Expand All @@ -53,13 +54,13 @@ GitHubArtifactProviderProperties githubArtifactProviderProperties() {
ArtifactCredentialsRepository artifactCredentialsRepository;

@Autowired
OkHttpClient okHttpClient;
OkHttpClient gitHubOkHttpClient;

@Autowired
ObjectMapper objectMapper;

@Bean
OkHttpClient okHttpClient() {
OkHttpClient gitHubOkHttpClient() {
return new OkHttpClient();
}

Expand All @@ -69,7 +70,7 @@ List<? extends GitHubArtifactCredentials> gitHubArtifactCredentials() {
.stream()
.map(a -> {
try {
GitHubArtifactCredentials c = new GitHubArtifactCredentials(a, okHttpClient, objectMapper);
GitHubArtifactCredentials c = new GitHubArtifactCredentials(a, gitHubOkHttpClient, objectMapper);
artifactCredentialsRepository.save(c);
return c;
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2018 Joel Wilsson
*
* 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.http;


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

@EqualsAndHashCode(callSuper = true)
@Data
public class HttpArtifactAccount extends ArtifactAccount {
private String name;
/*
One of the following are required for auth:
- username and password
- usernamePasswordFile : path to file containing "username:password"
*/
private String username;
private String password;
private String usernamePasswordFile;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2018 Joel Wilsson
*
* 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.http;

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.Scope;
import org.springframework.scheduling.annotation.EnableScheduling;

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

@Configuration
@ConditionalOnProperty("artifacts.http.enabled")
@EnableScheduling
@Slf4j
public class HttpArtifactConfiguration {
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
@ConfigurationProperties("artifacts.http")
HttpArtifactProviderProperties HttpArtifactProviderProperties() {
return new HttpArtifactProviderProperties();
}

@Autowired
HttpArtifactProviderProperties httpArtifactProviderProperties;

@Autowired
ArtifactCredentialsRepository artifactCredentialsRepository;

@Autowired
OkHttpClient httpOkHttpClient;

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

@Bean
List<? extends HttpArtifactCredentials> httpArtifactCredentials() {
return httpArtifactProviderProperties.getAccounts()
.stream()
.map(a -> {
try {
HttpArtifactCredentials c = new HttpArtifactCredentials(a, httpOkHttpClient);
artifactCredentialsRepository.save(c);
return c;
} catch (Exception e) {
log.warn("Failure instantiating Http 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,96 @@
/*
* Copyright 2018 Joel Wilsson
*
* 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.http;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
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.codec.binary.Base64;
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 HttpArtifactCredentials implements ArtifactCredentials {
private final String name;

@JsonIgnore
private final Builder requestBuilder;

@JsonIgnore
OkHttpClient okHttpClient;

public HttpArtifactCredentials(HttpArtifactAccount account, OkHttpClient okHttpClient) {
this.name = account.getName();
this.okHttpClient = okHttpClient;
Builder builder = new Request.Builder();
boolean useLogin = !StringUtils.isEmpty(account.getUsername()) && !StringUtils.isEmpty(account.getPassword());
boolean useUsernamePasswordFile = !StringUtils.isEmpty(account.getUsernamePasswordFile());
boolean useAuth = useLogin || useUsernamePasswordFile;
if (useAuth) {
String authHeader = "";
if (useUsernamePasswordFile) {
authHeader = "Basic " + Base64.encodeBase64String((credentialsFromFile(account.getUsernamePasswordFile())).getBytes());
} else if (useLogin) {
authHeader = "Basic " + Base64.encodeBase64String((account.getUsername() + ":" + account.getPassword()).getBytes());
}
builder.header("Authorization", authHeader);
log.info("Loaded credentials for http artifact account {}", account.getName());
} else {
log.info("No credentials included with http 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 http credentials file {}", filename);
return null;
}
}

public InputStream download(Artifact artifact) throws IOException {
Request downloadRequest = requestBuilder
.url(artifact.getReference())
.build();

Response downloadResponse = okHttpClient.newCall(downloadRequest).execute();
return downloadResponse.body().byteStream();
}

@Override
public boolean handlesType(String type) {
return type.equals("http/file");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2018 Joel Wilsson
*
* 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.http;

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

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

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

0 comments on commit 4753361

Please sign in to comment.