Skip to content

Commit

Permalink
fix(rollback): pass authentication context and consider errors fatal
Browse files Browse the repository at this point in the history
This avoids a potentially dangerous scenario where we roll back to a server
group of size 0 and fail to look it up for some reason, e.g. if it is in
a restricted account and we fail to pass the credentials along to clouddriver.
  • Loading branch information
dreynaud committed Oct 2, 2019
1 parent 695d6a2 commit cb11ede
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.netflix.spinnaker.orca.clouddriver.pipeline.servergroup.rollback

import com.fasterxml.jackson.annotation.JsonIgnore
import com.netflix.spinnaker.kork.core.RetrySupport
import com.netflix.spinnaker.kork.exceptions.SpinnakerException
import com.netflix.spinnaker.orca.clouddriver.pipeline.providers.aws.ApplySourceServerGroupCapacityStage
import com.netflix.spinnaker.orca.clouddriver.pipeline.providers.aws.CaptureSourceServerGroupCapacityStage
import com.netflix.spinnaker.orca.clouddriver.pipeline.servergroup.DisableServerGroupStage
Expand All @@ -29,14 +30,13 @@ import com.netflix.spinnaker.orca.kato.pipeline.support.ResizeStrategy
import com.netflix.spinnaker.orca.pipeline.WaitStage
import com.netflix.spinnaker.orca.pipeline.model.Stage
import com.netflix.spinnaker.orca.pipeline.model.SyntheticStageOwner
import groovy.transform.TimedInterrupt
import com.netflix.spinnaker.security.AuthenticatedRequest
import groovy.util.logging.Slf4j
import org.springframework.beans.factory.annotation.Autowired

import javax.annotation.Nullable
import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

import static com.netflix.spinnaker.orca.pipeline.StageDefinitionBuilder.newStage
Expand Down Expand Up @@ -155,17 +155,20 @@ class ExplicitRollback implements Rollback {
// we use an executor+future to timebox how long we can spend in this remote call
// because we are in a StartStage message and need to response quickly
// this is a bit of a hack, the proper long term fix would be to encapsulate this remote call in a task
executor.submit((Callable) {
Callable authenticatedRequest = AuthenticatedRequest.propagate({
return oortHelper.getTargetServerGroup(
fromContext.credentials,
serverGroupName,
fromContext.location
)
}).get(5, TimeUnit.SECONDS)
})

executor.submit(authenticatedRequest)
.get(5, TimeUnit.SECONDS)
.get() // not sure what would cause the Optional to not be present but we would catch and log it
} catch(Exception e) {
log.error('Skipping resize stage because there was an error looking up {}', serverGroupName, e)
return null
log.error('Could not generate resize stage because there was an error looking up {}', serverGroupName, e)
throw new SpinnakerException("failed to look up ${serverGroupName}", e)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.netflix.spinnaker.orca.clouddriver.tasks.servergroup

import com.netflix.discovery.converters.Auto
import com.netflix.spectator.api.Id
import com.netflix.spectator.api.Registry

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.netflix.spinnaker.orca.clouddriver.pipeline.servergroup.rollback

import com.netflix.spinnaker.kork.exceptions.SpinnakerException
import com.netflix.spinnaker.orca.clouddriver.pipeline.providers.aws.ApplySourceServerGroupCapacityStage
import com.netflix.spinnaker.orca.clouddriver.pipeline.providers.aws.CaptureSourceServerGroupCapacityStage
import com.netflix.spinnaker.orca.clouddriver.pipeline.servergroup.DisableServerGroupStage
Expand Down Expand Up @@ -191,6 +192,9 @@ class ExplicitRollbackSpec extends Specification {
def afterStages = allStages.findAll { it.syntheticStageOwner == SyntheticStageOwner.STAGE_AFTER }

then:
1 * oortHelper.getTargetServerGroup(_, rollbackServerGroupName, _) >> Optional.of(serverGroup(rollbackServerGroupName, 2))
1 * oortHelper.getTargetServerGroup(_, restoreServerGroupName, _) >> Optional.of(serverGroup(restoreServerGroupName, 1))

afterStages*.type.contains("wait") == waitStageExpected
afterStages*.type.indexOf("wait") < afterStages*.type.indexOf("disableServerGroup")

Expand All @@ -200,4 +204,13 @@ class ExplicitRollbackSpec extends Specification {
0 || false
1 || true
}

def "server group lookups failures are fatal"() {
when:
rollback.buildStages(stage)

then:
1 * oortHelper.getTargetServerGroup(_, rollbackServerGroupName, _) >> { throw new Exception(":(") }
thrown(SpinnakerException)
}
}

0 comments on commit cb11ede

Please sign in to comment.