Skip to content

Commit

Permalink
fix(web): Fix NPE in FeaturesController for operations that are not…
Browse files Browse the repository at this point in the history
… an `@AtomicOperation` or `@Component` (#5290)

We should be able to support beans that are created in `@Configuration` classes and are not
an `@AtomicOperation`.
  • Loading branch information
ajordens committed Mar 12, 2021
1 parent 74fbe6a commit 9904d5e
Showing 1 changed file with 43 additions and 15 deletions.
Expand Up @@ -18,7 +18,10 @@ package com.netflix.spinnaker.clouddriver.controllers

import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperationConverter
import groovy.util.logging.Slf4j
import org.springframework.beans.BeansException
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.core.annotation.AnnotationUtils
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.RequestMapping
Expand All @@ -28,33 +31,58 @@ import org.springframework.web.bind.annotation.RestController
@Slf4j
@RestController
@RequestMapping("/features")
class FeaturesController {
class FeaturesController implements ApplicationContextAware {
private ApplicationContext applicationContext

@Autowired
Collection<AtomicOperationConverter> atomicOperationConverters = []

@RequestMapping(value = "/stages", method = RequestMethod.GET)
Collection<Map> stages() {
return atomicOperationConverters.collect { AtomicOperationConverter atomicOperationConverter ->
def value = atomicOperationConverter.class.annotations.findResult {
def operationInterface = it.class.interfaces.find {
// look for a cloud provider-specific annotation indicating it's an AtomicOperation
it.name.endsWith("Operation")
try {
def value = atomicOperationConverter.class.annotations.findResult {
def operationInterface = it.class.interfaces.find {
// look for a cloud provider-specific annotation indicating it's an AtomicOperation
it.name.endsWith("Operation")
}

if (operationInterface) {
def annotation = atomicOperationConverter.class.getAnnotation(operationInterface)
return AnnotationUtils.getValue(annotation)
}

return null
}

if (operationInterface) {
def annotation = atomicOperationConverter.class.getAnnotation(operationInterface)
return AnnotationUtils.getValue(annotation)
value = value ?: atomicOperationConverter.class.getAnnotation(Component)?.value()
if (!value) {
def beanNames = applicationContext.getBeanNamesForType(atomicOperationConverter.class)
if (beanNames.size() == 1) {
value = beanNames[0]
} else {
// unable to determine bean/stage name, do not include it in available stages (very strange if it happens!)
value = atomicOperationConverter.class.simpleName
}
}

return null
return [
name : value,
enabled: true
]
} catch (Exception e) {
log.warn("Unable to determine bean/stage name for ${atomicOperationConverter.class}", e)
return [
name : atomicOperationConverter.class.simpleName,
enabled: true
]
}
}

value = value ?: atomicOperationConverter.class.getAnnotation(Component).value()
}

return [
name: value,
enabled: true
]
}
@Override
void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext
}
}

0 comments on commit 9904d5e

Please sign in to comment.