Skip to content

Commit

Permalink
feat(provider/oraclebmcs): Add initial scaffolding for Oracle BMCS pr…
Browse files Browse the repository at this point in the history
…ovider (#1532)
  • Loading branch information
simonlord authored and lwander committed Mar 30, 2017
1 parent 2865346 commit d3ecca8
Show file tree
Hide file tree
Showing 17 changed files with 500 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ application.yml
README.html
*~
es-tmp/
clouddriver-oracle-bmcs/bmcs-sdk
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Pivotal, Inc <*@pivotal.io>
Microsoft, Inc <*@microsoft.com>
Veritas Technologies, LLC <*@veritas.com>
Target, Inc <*@target.com>
Oracle America, Inc <*@oracle.com>
59 changes: 59 additions & 0 deletions clouddriver-oracle-bmcs/clouddriver-oracle-bmcs.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Oracle BMCS SDK isn't published to any maven repo (yet!), so we manually download, unpack and add to compile/runtime deps
def bmcsSDK = project.file('bmcs-sdk/lib/oracle-bmc-java-sdk-full-1.2.2.jar')
def sdkDest = "bmcs-sdk"

if (bmcsSDK.exists() == false) {
def sdkArchive = File.createTempFile("bmcsSDK","archive.zip")
sdkArchive << new URL("https://github.com/oracle/bmcs-java-sdk/releases/download/v1.2.2/oracle-bmcs-java-sdk.zip").openStream()

copy {
from zipTree(sdkArchive)
into sdkDest
include "**/*.jar"
exclude "**/*-sources.jar"
exclude "**/*-javadoc.jar"

// Scary but works. I think clouddriver deps in general need cleaning at some point
// Even without the oracle bmc sdk 3rd party deps there's still multiple javax.inject and commons-X JARs
exclude "**/*jackson*.jar"
exclude "**/*jersey*.jar"
exclude "**/hk2*.jar"
exclude "**/*guava*.jar"
exclude "**/commons*.jar"
exclude "**/javax*.jar"
exclude "**/aopalliance*.jar"
exclude "**/javassist*.jar"
exclude "**/slf*.jar"
exclude "**/osgi*.jar"
exclude "**/validation*.jar"
exclude "**/jsr305*.jar"
exclude "**/bcprov*.jar"

}
}

def bmcsSDKDeps = project.fileTree('bmcs-sdk/third-party/lib')

task cleanSDK << {
project.file(sdkDest).deleteDir()
}
clean.dependsOn cleanSDK



dependencies {
compile project(":clouddriver-core")
compile spinnaker.dependency('frigga')
compile spinnaker.dependency('bootActuator')
compile spinnaker.dependency('bootWeb')
compile files(bmcsSDK)
runtime files(bmcsSDKDeps)
}


license {
header project.file('oracle-source-header')
includes(["**/*.groovy", "**/*.java", "**/*.properties"])
strictCheck true
skipExistingHeaders false
}
6 changes: 6 additions & 0 deletions clouddriver-oracle-bmcs/oracle-source-header
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Copyright (c) 2017 Oracle America, Inc.

The contents of this file are subject to the Apache License Version 2.0 (the "License");
you may not use this file except in compliance with the License.
If a copy of the Apache License Version 2.0 was not distributed with this file,
You can obtain one at https://www.apache.org/licenses/LICENSE-2.0.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2017 Oracle America, Inc.
*
* The contents of this file are subject to the Apache License Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* If a copy of the Apache License Version 2.0 was not distributed with this file,
* You can obtain one at https://www.apache.org/licenses/LICENSE-2.0.html
*/
package com.netflix.spinnaker.clouddriver.oraclebmcs

import com.netflix.spinnaker.clouddriver.core.CloudProvider
import org.springframework.stereotype.Component

import java.lang.annotation.Annotation

/**
* OracleBMCS declaration as a {@link CloudProvider}.
*/
@Component
class OracleBMCSCloudProvider implements CloudProvider {

static final String ID = "oraclebmcs"
final String id = ID
final String displayName = ID
final Class<Annotation> operationAnnotationType = OracleBMCSOperation
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2017 Oracle America, Inc.
*
* The contents of this file are subject to the Apache License Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* If a copy of the Apache License Version 2.0 was not distributed with this file,
* You can obtain one at https://www.apache.org/licenses/LICENSE-2.0.html
*/
package com.netflix.spinnaker.clouddriver.oraclebmcs

import com.netflix.spinnaker.clouddriver.oraclebmcs.config.OracleBMCSConfigurationProperties
import com.netflix.spinnaker.clouddriver.oraclebmcs.health.OracleBMCSHealthIndicator
import com.netflix.spinnaker.clouddriver.oraclebmcs.security.OracleBMCSCredentialsInitializer
import org.springframework.beans.factory.config.ConfigurableBeanFactory
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.*
import org.springframework.scheduling.annotation.EnableScheduling

@Configuration
@EnableConfigurationProperties
@EnableScheduling
@ConditionalOnProperty('oraclebmcs.enabled')
@ComponentScan(["com.netflix.spinnaker.clouddriver.oraclebmcs"])
@Import([ OracleBMCSCredentialsInitializer ])
class OracleBMCSConfiguration {

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
@ConfigurationProperties("oraclebmcs")
OracleBMCSConfigurationProperties oracleBMCSConfigurationProperties() {
new OracleBMCSConfigurationProperties()
}

@Bean
OracleBMCSHealthIndicator OracleBMCSHealthIndicator() {
new OracleBMCSHealthIndicator()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2017 Oracle America, Inc.
*
* The contents of this file are subject to the Apache License Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* If a copy of the Apache License Version 2.0 was not distributed with this file,
* You can obtain one at https://www.apache.org/licenses/LICENSE-2.0.html
*/
package com.netflix.spinnaker.clouddriver.oraclebmcs

import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target

/**
* {@code OracleBMCSOperation}s specify implementation classes of Spinnaker AtomicOperations for OracleBMCS.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface OracleBMCSOperation {

String value()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2017 Oracle America, Inc.
*
* The contents of this file are subject to the Apache License Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* If a copy of the Apache License Version 2.0 was not distributed with this file,
* You can obtain one at https://www.apache.org/licenses/LICENSE-2.0.html
*/
package com.netflix.spinnaker.clouddriver.oraclebmcs.config

import groovy.transform.ToString

@ToString(includeNames = true)
class OracleBMCSConfigurationProperties {

@ToString(includeNames = true)
static class ManagedAccount {

String name
String environment
String accountType
List<String> requiredGroupMembership = []
String compartmentId
}

List<ManagedAccount> accounts = []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2017 Oracle America, Inc.
*
* The contents of this file are subject to the Apache License Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* If a copy of the Apache License Version 2.0 was not distributed with this file,
* You can obtain one at https://www.apache.org/licenses/LICENSE-2.0.html
*/
package com.netflix.spinnaker.clouddriver.oraclebmcs.deploy.converter

import com.netflix.spinnaker.clouddriver.oraclebmcs.OracleBMCSOperation
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperation
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperations
import com.netflix.spinnaker.clouddriver.security.AbstractAtomicOperationsCredentialsSupport
import org.springframework.stereotype.Component

/**
* SL: Adding this for initial pull request to satisfy FeatureController requirement of at least
* one atomic operation enabled when starting clouddriver with just oraclebmcs enabled.
*/
@OracleBMCSOperation(AtomicOperations.DELETE_SECURITY_GROUP)
@Component("noopOracleBMCSDescription")
class NoOpOracleBMCSAtomicOperationConverter extends AbstractAtomicOperationsCredentialsSupport {

@Override
AtomicOperation convertOperation(Map input) {
return null
}

@Override
Object convertDescription(Map input) {
return null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2017 Oracle America, Inc.
*
* The contents of this file are subject to the Apache License Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* If a copy of the Apache License Version 2.0 was not distributed with this file,
* You can obtain one at https://www.apache.org/licenses/LICENSE-2.0.html
*/
package com.netflix.spinnaker.clouddriver.oraclebmcs.health

import com.netflix.spinnaker.clouddriver.oraclebmcs.security.OracleBMCSNamedAccountCredentials
import com.netflix.spinnaker.clouddriver.security.AccountCredentialsProvider
import com.oracle.bmc.identity.requests.ListAvailabilityDomainsRequest
import groovy.transform.InheritConstructors
import groovy.util.logging.Slf4j
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.actuate.health.Health
import org.springframework.boot.actuate.health.HealthIndicator
import org.springframework.http.HttpStatus
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.ResponseStatus

import java.util.concurrent.atomic.AtomicReference

@Slf4j
@Component
class OracleBMCSHealthIndicator implements HealthIndicator {

@Autowired
AccountCredentialsProvider accountCredentialsProvider

private final AtomicReference<Exception> lastException = new AtomicReference<>(null)

@Override
Health health() {
def ex = lastException.get()

if (ex) {
throw ex
}

new Health.Builder().up().build()
}

@Scheduled(fixedDelay = 300000L)
void checkHealth() {
try {
Set<OracleBMCSNamedAccountCredentials> creds = accountCredentialsProvider.all.findAll {
it instanceof OracleBMCSNamedAccountCredentials
} as Set<OracleBMCSNamedAccountCredentials>

creds.each { OracleBMCSNamedAccountCredentials cred ->
try {
cred.identityClient.listAvailabilityDomains(ListAvailabilityDomainsRequest.builder().compartmentId(cred.compartmentId).build())
} catch(Exception ex) {
throw new OracleBMCSUnreachableException(ex)
}
}

lastException.set(null)
} catch (Exception ex) {
log.warn "Unhealthy", ex
lastException.set(ex)
}
}

@ResponseStatus(value = HttpStatus.SERVICE_UNAVAILABLE, reason = 'Could not reach the Oracle BMCS service.')
@InheritConstructors
static class OracleBMCSUnreachableException extends RuntimeException {}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2017 Oracle America, Inc.
*
* The contents of this file are subject to the Apache License Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* If a copy of the Apache License Version 2.0 was not distributed with this file,
* You can obtain one at https://www.apache.org/licenses/LICENSE-2.0.html
*/
package com.netflix.spinnaker.clouddriver.oraclebmcs.security

import com.netflix.spinnaker.cats.module.CatsModule
import com.netflix.spinnaker.clouddriver.oraclebmcs.config.OracleBMCSConfigurationProperties
import com.netflix.spinnaker.clouddriver.security.AccountCredentialsRepository
import com.netflix.spinnaker.clouddriver.security.CredentialsInitializerSynchronizable
import com.netflix.spinnaker.clouddriver.security.ProviderUtils
import groovy.util.logging.Slf4j
import org.springframework.beans.factory.config.ConfigurableBeanFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Scope

@Slf4j
@Configuration
class OracleBMCSCredentialsInitializer implements CredentialsInitializerSynchronizable {

@Bean
List<? extends OracleBMCSNamedAccountCredentials> oracleBMCSNamedAccountCredentials(
String clouddriverUserAgentApplicationName,
OracleBMCSConfigurationProperties oracleBMCSConfigurationProperties,
AccountCredentialsRepository accountCredentialsRepository
) {
synchronizeOracleBMCSAccounts(clouddriverUserAgentApplicationName, oracleBMCSConfigurationProperties, null, accountCredentialsRepository)
}

@Override
String getCredentialsSynchronizationBeanName() {
return "synchronizeOracleBMCSAccounts"
}

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
List<? extends OracleBMCSNamedAccountCredentials> synchronizeOracleBMCSAccounts(String clouddriverUserAgentApplicationName,
OracleBMCSConfigurationProperties oracleBMCSConfigurationProperties,
CatsModule catsModule,
AccountCredentialsRepository accountCredentialsRepository) {

def (ArrayList<OracleBMCSConfigurationProperties.ManagedAccount> accountsToAdd, List<String> namesOfDeletedAccounts) =
ProviderUtils.calculateAccountDeltas(accountCredentialsRepository,
OracleBMCSNamedAccountCredentials.class,
oracleBMCSConfigurationProperties.accounts)

accountsToAdd.each { OracleBMCSConfigurationProperties.ManagedAccount managedAccount ->
try {
def oracleBMCSAccount = new OracleBMCSNamedAccountCredentials.Builder().name(managedAccount.name).
environment(managedAccount.environment ?: managedAccount.name).
accountType(managedAccount.accountType ?: managedAccount.name).
requiredGroupMembership(managedAccount.requiredGroupMembership).
compartmentID(managedAccount.compartmentId).
build()

accountCredentialsRepository.save(managedAccount.name, oracleBMCSAccount)
} catch (e) {
log.info("Could not load account $managedAccount.name for OracleBMCS", e)
}
}

ProviderUtils.unscheduleAndDeregisterAgents(namesOfDeletedAccounts, catsModule)

accountCredentialsRepository.all.findAll {
it instanceof OracleBMCSNamedAccountCredentials
} as List<OracleBMCSNamedAccountCredentials>
}
}
Loading

0 comments on commit d3ecca8

Please sign in to comment.