Merge branch 'cache-cred-mgr-lookups' of https://github.com/ralekseenkov/concourse into ralekseenkov-cache-cred-mgr-lookups
Signed-off-by: Alex Suraci <suraci.alex@gmail.com>
This commit is contained in:
commit
f2a3c3a9f5
|
@ -49,7 +49,7 @@ var (
|
|||
dbBuildFactory *dbfakes.FakeBuildFactory
|
||||
dbTeam *dbfakes.FakeTeam
|
||||
fakeScannerFactory *resourceserverfakes.FakeScannerFactory
|
||||
fakeVariablesFactory *credsfakes.FakeVariablesFactory
|
||||
fakeSecretManager *credsfakes.FakeSecrets
|
||||
credsManagers creds.Managers
|
||||
interceptTimeoutFactory *containerserverfakes.FakeInterceptTimeoutFactory
|
||||
interceptTimeout *containerserverfakes.FakeInterceptTimeout
|
||||
|
@ -116,7 +116,7 @@ var _ = BeforeEach(func() {
|
|||
fakeContainerRepository = new(dbfakes.FakeContainerRepository)
|
||||
fakeDestroyer = new(gcfakes.FakeDestroyer)
|
||||
|
||||
fakeVariablesFactory = new(credsfakes.FakeVariablesFactory)
|
||||
fakeSecretManager = new(credsfakes.FakeSecrets)
|
||||
credsManagers = make(creds.Managers)
|
||||
var err error
|
||||
|
||||
|
@ -179,7 +179,7 @@ var _ = BeforeEach(func() {
|
|||
cliDownloadsDir,
|
||||
"1.2.3",
|
||||
"4.5.6",
|
||||
fakeVariablesFactory,
|
||||
fakeSecretManager,
|
||||
credsManagers,
|
||||
interceptTimeoutFactory,
|
||||
)
|
||||
|
|
|
@ -8,10 +8,10 @@ import (
|
|||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"time"
|
||||
|
||||
"github.com/concourse/concourse/atc"
|
||||
"github.com/concourse/concourse/atc/api/accessor/accessorfakes"
|
||||
"github.com/concourse/concourse/atc/creds/credsfakes"
|
||||
"github.com/concourse/concourse/atc/creds/noop"
|
||||
"github.com/concourse/concourse/atc/db"
|
||||
"github.com/concourse/concourse/atc/db/dbfakes"
|
||||
|
@ -737,9 +737,7 @@ jobs:
|
|||
|
||||
Context("when the credential exists in the credential manager", func() {
|
||||
BeforeEach(func() {
|
||||
fakeVariables := new(credsfakes.FakeVariables)
|
||||
fakeVariablesFactory.NewVariablesReturns(fakeVariables)
|
||||
fakeVariables.GetReturns("this-string-value-doesn't-matter", true, nil)
|
||||
fakeSecretManager.GetReturns("this-string-value-doesn't-matter", nil, true, nil)
|
||||
})
|
||||
|
||||
It("passes validation and saves it un-interpolated", func() {
|
||||
|
@ -760,9 +758,7 @@ jobs:
|
|||
|
||||
Context("when the credential does not exist in the credential manager", func() {
|
||||
BeforeEach(func() {
|
||||
fakeVariables := new(credsfakes.FakeVariables)
|
||||
fakeVariablesFactory.NewVariablesReturns(fakeVariables)
|
||||
fakeVariables.GetReturns(nil, false, nil) // nil value, not found, no error
|
||||
fakeSecretManager.GetReturns(nil, nil, false, nil) // nil value, nil expiration, not found, no error
|
||||
})
|
||||
|
||||
It("returns 400", func() {
|
||||
|
@ -776,8 +772,9 @@ jobs:
|
|||
|
||||
Context("when a credentials manager is not used", func() {
|
||||
BeforeEach(func() {
|
||||
fakeVariables := noop.Noop{}
|
||||
fakeVariablesFactory.NewVariablesReturns(&fakeVariables)
|
||||
fakeSecretManager.GetStub = func(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
return noop.Noop{}.Get(secretPath)
|
||||
}
|
||||
})
|
||||
|
||||
It("returns 400", func() {
|
||||
|
|
|
@ -118,7 +118,7 @@ func (s *Server) SaveConfig(w http.ResponseWriter, r *http.Request) {
|
|||
teamName := rata.Param(r, "team_name")
|
||||
|
||||
if checkCredentials {
|
||||
variables := s.variablesFactory.NewVariables(teamName, pipelineName)
|
||||
variables := creds.NewVariables(s.secretManager, teamName, pipelineName)
|
||||
|
||||
errs := validateCredParams(variables, config, session)
|
||||
if errs != nil {
|
||||
|
|
|
@ -7,19 +7,19 @@ import (
|
|||
)
|
||||
|
||||
type Server struct {
|
||||
logger lager.Logger
|
||||
teamFactory db.TeamFactory
|
||||
variablesFactory creds.VariablesFactory
|
||||
logger lager.Logger
|
||||
teamFactory db.TeamFactory
|
||||
secretManager creds.Secrets
|
||||
}
|
||||
|
||||
func NewServer(
|
||||
logger lager.Logger,
|
||||
teamFactory db.TeamFactory,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
) *Server {
|
||||
return &Server{
|
||||
logger: logger,
|
||||
teamFactory: teamFactory,
|
||||
variablesFactory: variablesFactory,
|
||||
logger: logger,
|
||||
teamFactory: teamFactory,
|
||||
secretManager: secretManager,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -362,10 +362,10 @@ var _ = Describe("Containers API", func() {
|
|||
_, err := client.Do(req)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, pipelineName, resourceName, variablesFactory := dbTeam.FindCheckContainersArgsForCall(0)
|
||||
_, pipelineName, resourceName, secretManager := dbTeam.FindCheckContainersArgsForCall(0)
|
||||
Expect(pipelineName).To(Equal("some-pipeline"))
|
||||
Expect(resourceName).To(Equal("some-resource"))
|
||||
Expect(variablesFactory).To(Equal(fakeVariablesFactory))
|
||||
Expect(secretManager).To(Equal(fakeSecretManager))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -21,7 +21,7 @@ func (s *Server) ListContainers(team db.Team) http.Handler {
|
|||
"params": params,
|
||||
})
|
||||
|
||||
containerLocator, err := createContainerLocatorFromRequest(team, r, s.variablesFactory)
|
||||
containerLocator, err := createContainerLocatorFromRequest(team, r, s.secretManager)
|
||||
if err != nil {
|
||||
hLog.Error("failed-to-parse-request", err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
|
@ -59,7 +59,7 @@ type containerLocator interface {
|
|||
Locate(logger lager.Logger) ([]db.Container, map[int]time.Time, error)
|
||||
}
|
||||
|
||||
func createContainerLocatorFromRequest(team db.Team, r *http.Request, variablesFactory creds.VariablesFactory) (containerLocator, error) {
|
||||
func createContainerLocatorFromRequest(team db.Team, r *http.Request, secretManager creds.Secrets) (containerLocator, error) {
|
||||
query := r.URL.Query()
|
||||
delete(query, ":team_name")
|
||||
|
||||
|
@ -71,10 +71,10 @@ func createContainerLocatorFromRequest(team db.Team, r *http.Request, variablesF
|
|||
|
||||
if query.Get("type") == "check" {
|
||||
return &checkContainerLocator{
|
||||
team: team,
|
||||
pipelineName: query.Get("pipeline_name"),
|
||||
resourceName: query.Get("resource_name"),
|
||||
variablesFactory: variablesFactory,
|
||||
team: team,
|
||||
pipelineName: query.Get("pipeline_name"),
|
||||
resourceName: query.Get("resource_name"),
|
||||
secretManager: secretManager,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -132,14 +132,14 @@ func (l *allContainersLocator) Locate(logger lager.Logger) ([]db.Container, map[
|
|||
}
|
||||
|
||||
type checkContainerLocator struct {
|
||||
team db.Team
|
||||
pipelineName string
|
||||
resourceName string
|
||||
variablesFactory creds.VariablesFactory
|
||||
team db.Team
|
||||
pipelineName string
|
||||
resourceName string
|
||||
secretManager creds.Secrets
|
||||
}
|
||||
|
||||
func (l *checkContainerLocator) Locate(logger lager.Logger) ([]db.Container, map[int]time.Time, error) {
|
||||
return l.team.FindCheckContainers(logger, l.pipelineName, l.resourceName, l.variablesFactory)
|
||||
return l.team.FindCheckContainers(logger, l.pipelineName, l.resourceName, l.secretManager)
|
||||
}
|
||||
|
||||
type stepContainerLocator struct {
|
||||
|
|
|
@ -12,7 +12,7 @@ type Server struct {
|
|||
logger lager.Logger
|
||||
|
||||
workerClient worker.Client
|
||||
variablesFactory creds.VariablesFactory
|
||||
secretManager creds.Secrets
|
||||
interceptTimeoutFactory InterceptTimeoutFactory
|
||||
containerRepository db.ContainerRepository
|
||||
destroyer gc.Destroyer
|
||||
|
@ -21,7 +21,7 @@ type Server struct {
|
|||
func NewServer(
|
||||
logger lager.Logger,
|
||||
workerClient worker.Client,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
interceptTimeoutFactory InterceptTimeoutFactory,
|
||||
containerRepository db.ContainerRepository,
|
||||
destroyer gc.Destroyer,
|
||||
|
@ -29,7 +29,7 @@ func NewServer(
|
|||
return &Server{
|
||||
logger: logger,
|
||||
workerClient: workerClient,
|
||||
variablesFactory: variablesFactory,
|
||||
secretManager: secretManager,
|
||||
interceptTimeoutFactory: interceptTimeoutFactory,
|
||||
containerRepository: containerRepository,
|
||||
destroyer: destroyer,
|
||||
|
|
|
@ -61,7 +61,7 @@ func NewHandler(
|
|||
cliDownloadsDir string,
|
||||
version string,
|
||||
workerVersion string,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
credsManagers creds.Managers,
|
||||
interceptTimeoutFactory containerserver.InterceptTimeoutFactory,
|
||||
) (http.Handler, error) {
|
||||
|
@ -76,16 +76,17 @@ func NewHandler(
|
|||
teamHandlerFactory := NewTeamScopedHandlerFactory(logger, dbTeamFactory)
|
||||
|
||||
buildServer := buildserver.NewServer(logger, externalURL, dbTeamFactory, dbBuildFactory, eventHandlerFactory)
|
||||
jobServer := jobserver.NewServer(logger, externalURL, variablesFactory, dbJobFactory)
|
||||
resourceServer := resourceserver.NewServer(logger, scannerFactory, variablesFactory, dbResourceFactory, dbResourceConfigFactory)
|
||||
jobServer := jobserver.NewServer(logger, externalURL, secretManager, dbJobFactory)
|
||||
resourceServer := resourceserver.NewServer(logger, scannerFactory, secretManager, dbResourceFactory, dbResourceConfigFactory)
|
||||
|
||||
versionServer := versionserver.NewServer(logger, externalURL)
|
||||
pipelineServer := pipelineserver.NewServer(logger, dbTeamFactory, dbPipelineFactory, externalURL)
|
||||
configServer := configserver.NewServer(logger, dbTeamFactory, variablesFactory)
|
||||
configServer := configserver.NewServer(logger, dbTeamFactory, secretManager)
|
||||
ccServer := ccserver.NewServer(logger, dbTeamFactory, externalURL)
|
||||
workerServer := workerserver.NewServer(logger, dbTeamFactory, dbWorkerFactory)
|
||||
logLevelServer := loglevelserver.NewServer(logger, sink)
|
||||
cliServer := cliserver.NewServer(logger, absCLIDownloadsDir)
|
||||
containerServer := containerserver.NewServer(logger, workerClient, variablesFactory, interceptTimeoutFactory, containerRepository, destroyer)
|
||||
containerServer := containerserver.NewServer(logger, workerClient, secretManager, interceptTimeoutFactory, containerRepository, destroyer)
|
||||
volumesServer := volumeserver.NewServer(logger, volumeRepository, destroyer)
|
||||
teamServer := teamserver.NewServer(logger, dbTeamFactory, externalURL)
|
||||
infoServer := infoserver.NewServer(logger, version, workerVersion, credsManagers)
|
||||
|
|
|
@ -107,7 +107,7 @@ var _ = Describe("Pipelines API", func() {
|
|||
fakeaccess.IsAuthenticatedReturns(true)
|
||||
fakeaccess.IsAdminReturns(true)
|
||||
|
||||
ssmAccess := ssm.NewSsm(lager.NewLogger("ssm_test"), &mockService, "alpha", "bogus", nil)
|
||||
ssmAccess := ssm.NewSsm(lager.NewLogger("ssm_test"), &mockService, nil)
|
||||
ssmManager := &ssm.SsmManager{
|
||||
AwsAccessKeyID: "",
|
||||
AwsSecretAccessKey: "",
|
||||
|
@ -193,8 +193,6 @@ var _ = Describe("Pipelines API", func() {
|
|||
vaultManager := &vault.VaultManager{
|
||||
URL: credServer.URL(),
|
||||
PathPrefix: "testpath",
|
||||
Cache: false,
|
||||
MaxLease: 60,
|
||||
TLS: tls,
|
||||
Auth: authConfig,
|
||||
}
|
||||
|
@ -264,8 +262,6 @@ var _ = Describe("Pipelines API", func() {
|
|||
"vault": {
|
||||
"url": "` + credServer.URL() + `",
|
||||
"path_prefix": "testpath",
|
||||
"cache": false,
|
||||
"max_lease": 60,
|
||||
"ca_cert": "",
|
||||
"server_name": "server-name",
|
||||
"auth_backend": "backend-server",
|
||||
|
@ -401,7 +397,7 @@ var _ = Describe("Pipelines API", func() {
|
|||
fakeaccess.IsAuthenticatedReturns(true)
|
||||
fakeaccess.IsAdminReturns(true)
|
||||
|
||||
secretsManagerAccess := secretsmanager.NewSecretsManager(lager.NewLogger("ssm_test"), &mockService, "alpha", "bogus", nil)
|
||||
secretsManagerAccess := secretsmanager.NewSecretsManager(lager.NewLogger("ssm_test"), &mockService, nil)
|
||||
|
||||
secretsManager := &secretsmanager.Manager{
|
||||
AwsAccessKeyID: "",
|
||||
|
|
|
@ -10,23 +10,23 @@ import (
|
|||
type Server struct {
|
||||
logger lager.Logger
|
||||
|
||||
externalURL string
|
||||
rejector auth.Rejector
|
||||
variablesFactory creds.VariablesFactory
|
||||
jobFactory db.JobFactory
|
||||
externalURL string
|
||||
rejector auth.Rejector
|
||||
secretManager creds.Secrets
|
||||
jobFactory db.JobFactory
|
||||
}
|
||||
|
||||
func NewServer(
|
||||
logger lager.Logger,
|
||||
externalURL string,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
jobFactory db.JobFactory,
|
||||
) *Server {
|
||||
return &Server{
|
||||
logger: logger,
|
||||
externalURL: externalURL,
|
||||
rejector: auth.UnauthorizedRejector{},
|
||||
variablesFactory: variablesFactory,
|
||||
jobFactory: jobFactory,
|
||||
logger: logger,
|
||||
externalURL: externalURL,
|
||||
rejector: auth.UnauthorizedRejector{},
|
||||
secretManager: secretManager,
|
||||
jobFactory: jobFactory,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ func (s *Server) CheckResourceWebHook(dbPipeline db.Pipeline) http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
variables := s.variablesFactory.NewVariables(dbPipeline.TeamName(), dbPipeline.Name())
|
||||
variables := creds.NewVariables(s.secretManager, dbPipeline.TeamName(), dbPipeline.Name())
|
||||
token, err := creds.NewString(variables, pipelineResource.WebhookToken()).Evaluate()
|
||||
if token != webhookToken {
|
||||
logger.Info("invalid-token", lager.Data{"error": fmt.Sprintf("invalid token for webhook %s", webhookToken)})
|
||||
|
|
|
@ -17,7 +17,7 @@ type ScannerFactory interface {
|
|||
type Server struct {
|
||||
logger lager.Logger
|
||||
scannerFactory ScannerFactory
|
||||
variablesFactory creds.VariablesFactory
|
||||
secretManager creds.Secrets
|
||||
resourceFactory db.ResourceFactory
|
||||
resourceConfigFactory db.ResourceConfigFactory
|
||||
}
|
||||
|
@ -25,14 +25,14 @@ type Server struct {
|
|||
func NewServer(
|
||||
logger lager.Logger,
|
||||
scannerFactory ScannerFactory,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
resourceFactory db.ResourceFactory,
|
||||
resourceConfigFactory db.ResourceConfigFactory,
|
||||
) *Server {
|
||||
return &Server{
|
||||
logger: logger,
|
||||
scannerFactory: scannerFactory,
|
||||
variablesFactory: variablesFactory,
|
||||
secretManager: secretManager,
|
||||
resourceFactory: resourceFactory,
|
||||
resourceConfigFactory: resourceConfigFactory,
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ import (
|
|||
"github.com/concourse/flag"
|
||||
"github.com/concourse/retryhttp"
|
||||
"github.com/cppforlife/go-semi-semantic/version"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/jessevdk/go-flags"
|
||||
"github.com/tedsuo/ifrit"
|
||||
"github.com/tedsuo/ifrit/grouper"
|
||||
"github.com/tedsuo/ifrit/http_server"
|
||||
|
@ -393,12 +393,12 @@ func (cmd *RunCommand) Runner(positionalArguments []string) (ifrit.Runner, error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
variablesFactory, err := cmd.variablesFactory(logger)
|
||||
secretManager, err := cmd.secretManager(logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
members, err := cmd.constructMembers(logger, reconfigurableSink, apiConn, backendConn, storage, lockFactory, variablesFactory)
|
||||
members, err := cmd.constructMembers(logger, reconfigurableSink, apiConn, backendConn, storage, lockFactory, secretManager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -440,7 +440,7 @@ func (cmd *RunCommand) constructMembers(
|
|||
backendConn db.Conn,
|
||||
storage storage.Storage,
|
||||
lockFactory lock.LockFactory,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
) ([]grouper.Member, error) {
|
||||
if cmd.TelemetryOptIn {
|
||||
url := fmt.Sprintf("http://telemetry.concourse-ci.org/?version=%s", concourse.Version)
|
||||
|
@ -452,12 +452,12 @@ func (cmd *RunCommand) constructMembers(
|
|||
}()
|
||||
}
|
||||
|
||||
apiMembers, err := cmd.constructAPIMembers(logger, reconfigurableSink, apiConn, storage, lockFactory, variablesFactory)
|
||||
apiMembers, err := cmd.constructAPIMembers(logger, reconfigurableSink, apiConn, storage, lockFactory, secretManager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backendMembers, err := cmd.constructBackendMembers(logger, backendConn, lockFactory, variablesFactory)
|
||||
backendMembers, err := cmd.constructBackendMembers(logger, backendConn, lockFactory, secretManager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -471,7 +471,7 @@ func (cmd *RunCommand) constructAPIMembers(
|
|||
dbConn db.Conn,
|
||||
storage storage.Storage,
|
||||
lockFactory lock.LockFactory,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
) ([]grouper.Member, error) {
|
||||
teamFactory := db.NewTeamFactory(dbConn, lockFactory)
|
||||
|
||||
|
@ -549,7 +549,7 @@ func (cmd *RunCommand) constructAPIMembers(
|
|||
cmd.ResourceTypeCheckingInterval,
|
||||
cmd.ResourceCheckingInterval,
|
||||
cmd.ExternalURL.String(),
|
||||
variablesFactory,
|
||||
secretManager,
|
||||
checkContainerStrategy,
|
||||
)
|
||||
|
||||
|
@ -577,7 +577,7 @@ func (cmd *RunCommand) constructAPIMembers(
|
|||
dbResourceConfigFactory,
|
||||
workerClient,
|
||||
radarScannerFactory,
|
||||
variablesFactory,
|
||||
secretManager,
|
||||
credsManagers,
|
||||
accessFactory,
|
||||
)
|
||||
|
@ -664,7 +664,7 @@ func (cmd *RunCommand) constructBackendMembers(
|
|||
logger lager.Logger,
|
||||
dbConn db.Conn,
|
||||
lockFactory lock.LockFactory,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
) ([]grouper.Member, error) {
|
||||
|
||||
if cmd.Syslog.Address != "" && cmd.Syslog.Transport == "" {
|
||||
|
@ -731,7 +731,7 @@ func (cmd *RunCommand) constructBackendMembers(
|
|||
resourceFetcher,
|
||||
dbResourceCacheFactory,
|
||||
dbResourceConfigFactory,
|
||||
variablesFactory,
|
||||
secretManager,
|
||||
defaultLimits,
|
||||
buildContainerStrategy,
|
||||
resourceFactory,
|
||||
|
@ -760,7 +760,7 @@ func (cmd *RunCommand) constructBackendMembers(
|
|||
logger.Session("pipelines"),
|
||||
dbPipelineFactory,
|
||||
radarSchedulerFactory,
|
||||
variablesFactory,
|
||||
secretManager,
|
||||
bus,
|
||||
),
|
||||
Interval: 10 * time.Second,
|
||||
|
@ -857,8 +857,8 @@ func workerVersion() (version.Version, error) {
|
|||
return version.NewVersionFromString(concourse.WorkerVersion)
|
||||
}
|
||||
|
||||
func (cmd *RunCommand) variablesFactory(logger lager.Logger) (creds.VariablesFactory, error) {
|
||||
var variablesFactory creds.VariablesFactory = noop.NewNoopFactory()
|
||||
func (cmd *RunCommand) secretManager(logger lager.Logger) (creds.Secrets, error) {
|
||||
var secretsFactory creds.SecretsFactory = noop.NewNoopFactory()
|
||||
for name, manager := range cmd.CredentialManagers {
|
||||
if !manager.IsConfigured() {
|
||||
continue
|
||||
|
@ -880,14 +880,20 @@ func (cmd *RunCommand) variablesFactory(logger lager.Logger) (creds.VariablesFac
|
|||
return nil, fmt.Errorf("credential manager '%s' misconfigured: %s", name, err)
|
||||
}
|
||||
|
||||
variablesFactory, err = manager.NewVariablesFactory(credsLogger)
|
||||
secretsFactory, err = manager.NewSecretsFactory(credsLogger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
return creds.NewRetryableVariablesFactory(variablesFactory, cmd.CredentialManagement.RetryConfig), nil
|
||||
|
||||
result := secretsFactory.NewSecrets()
|
||||
result = creds.NewRetryableSecrets(result, cmd.CredentialManagement.RetryConfig)
|
||||
if cmd.CredentialManagement.CacheConfig.Enabled {
|
||||
result = creds.NewCachedSecrets(result, cmd.CredentialManagement.CacheConfig)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (cmd *RunCommand) newKey() *encryption.Key {
|
||||
|
@ -1172,7 +1178,7 @@ func (cmd *RunCommand) constructEngine(
|
|||
resourceFetcher resource.Fetcher,
|
||||
resourceCacheFactory db.ResourceCacheFactory,
|
||||
resourceConfigFactory db.ResourceConfigFactory,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
defaultLimits atc.ContainerLimits,
|
||||
strategy worker.ContainerPlacementStrategy,
|
||||
resourceFactory resource.ResourceFactory,
|
||||
|
@ -1184,7 +1190,7 @@ func (cmd *RunCommand) constructEngine(
|
|||
resourceFetcher,
|
||||
resourceCacheFactory,
|
||||
resourceConfigFactory,
|
||||
variablesFactory,
|
||||
secretManager,
|
||||
defaultLimits,
|
||||
strategy,
|
||||
resourceFactory,
|
||||
|
@ -1245,7 +1251,7 @@ func (cmd *RunCommand) constructAPIHandler(
|
|||
resourceConfigFactory db.ResourceConfigFactory,
|
||||
workerClient worker.Client,
|
||||
radarScannerFactory radar.ScannerFactory,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
credsManagers creds.Managers,
|
||||
accessFactory accessor.AccessFactory,
|
||||
) (http.Handler, error) {
|
||||
|
@ -1295,7 +1301,7 @@ func (cmd *RunCommand) constructAPIHandler(
|
|||
cmd.CLIArtifactsDir.Path(),
|
||||
concourse.Version,
|
||||
concourse.WorkerVersion,
|
||||
variablesFactory,
|
||||
secretManager,
|
||||
credsManagers,
|
||||
containerserver.NewInterceptTimeoutFactory(cmd.InterceptIdleTimeout),
|
||||
)
|
||||
|
@ -1326,14 +1332,14 @@ func (cmd *RunCommand) constructPipelineSyncer(
|
|||
logger lager.Logger,
|
||||
pipelineFactory db.PipelineFactory,
|
||||
radarSchedulerFactory pipelines.RadarSchedulerFactory,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
bus db.NotificationsBus,
|
||||
) *pipelines.Syncer {
|
||||
return pipelines.NewSyncer(
|
||||
logger,
|
||||
pipelineFactory,
|
||||
func(pipeline db.Pipeline) ifrit.Runner {
|
||||
variables := variablesFactory.NewVariables(pipeline.TeamName(), pipeline.Name())
|
||||
variables := creds.NewVariables(secretManager, pipeline.TeamName(), pipeline.Name())
|
||||
return grouper.NewParallel(os.Interrupt, grouper.Members{
|
||||
{
|
||||
Name: fmt.Sprintf("radar:%d", pipeline.ID()),
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
package creds
|
||||
|
||||
import (
|
||||
"github.com/patrickmn/go-cache"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SecretCacheConfig struct {
|
||||
Enabled bool `long:"secret-cache-enabled" description:"Enable in-memory cache for secrets"`
|
||||
Duration time.Duration `long:"secret-cache-duration" default:"1m" description:"If the cache is enabled, secret values will be cached for not longer than this duration (it can be less, if underlying secret lease time is smaller)"`
|
||||
PurgeInterval time.Duration `long:"secret-cache-purge-interval" default:"10m" description:"If the cache is enabled, expired items will be removed on this internal"`
|
||||
}
|
||||
|
||||
type CachedSecrets struct {
|
||||
secrets Secrets
|
||||
cacheConfig SecretCacheConfig
|
||||
cache *cache.Cache
|
||||
}
|
||||
|
||||
type CacheEntry struct {
|
||||
value interface{}
|
||||
expiration *time.Time
|
||||
found bool
|
||||
}
|
||||
|
||||
func NewCachedSecrets(secrets Secrets, cacheConfig SecretCacheConfig) *CachedSecrets {
|
||||
// Create a cache with:
|
||||
// - default expiration time for entries set to 'cacheConfig.Duration'
|
||||
// - purges expired items regularly, on every `cacheConfig.PurgeInterval` after creation
|
||||
return &CachedSecrets{
|
||||
secrets: secrets,
|
||||
cacheConfig: cacheConfig,
|
||||
cache: cache.New(cacheConfig.Duration, cacheConfig.PurgeInterval),
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *CachedSecrets) Get(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
// if there is a corresponding entry in the cache, return it
|
||||
entry, found := cs.cache.Get(secretPath)
|
||||
if found {
|
||||
result := entry.(CacheEntry)
|
||||
return result.value, result.expiration, result.found, nil
|
||||
}
|
||||
|
||||
// otherwise, let's make a request to the underlying secret manager
|
||||
value, expiration, found, err := cs.secrets.Get(secretPath)
|
||||
|
||||
// we don't want to cache errors, let the errors be retried the next time around
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
// here we want to cache secret value, expiration, and found flag too
|
||||
// meaning that "secret not found" responses will be cached too!
|
||||
entry = CacheEntry{value: value, expiration: expiration, found: found}
|
||||
|
||||
// take default cache ttl
|
||||
duration := cs.cacheConfig.Duration
|
||||
if expiration != nil {
|
||||
// if secret lease time expires sooner, make duration smaller than default duration
|
||||
itemDuration := expiration.Sub(time.Now())
|
||||
if itemDuration < duration {
|
||||
duration = itemDuration
|
||||
}
|
||||
}
|
||||
|
||||
cs.cache.Set(secretPath, entry, duration)
|
||||
return value, expiration, found, nil
|
||||
}
|
||||
|
||||
func (cs *CachedSecrets) NewSecretLookupPaths(teamName string, pipelineName string) []SecretLookupPath {
|
||||
return cs.secrets.NewSecretLookupPaths(teamName, pipelineName)
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package creds_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"github.com/concourse/concourse/atc/creds/credsfakes"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func makeGetStub(name string, value interface{}, expiration *time.Time, found bool, err error, cntReads *int, cntMisses *int) func(string) (interface{}, *time.Time, bool, error) {
|
||||
return func(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
if secretPath == name {
|
||||
*cntReads++
|
||||
return value, expiration, found, err
|
||||
}
|
||||
*cntMisses++
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
var _ = Describe("Caching of secrets", func() {
|
||||
|
||||
var secretManager *credsfakes.FakeSecrets
|
||||
var cachedSecretManager *creds.CachedSecrets
|
||||
var underlyingReads int
|
||||
var underlyingMisses int
|
||||
|
||||
BeforeEach(func() {
|
||||
secretManager = new(credsfakes.FakeSecrets)
|
||||
cachedSecretManager = creds.NewCachedSecrets(secretManager, creds.SecretCacheConfig{
|
||||
Duration: 2 * time.Second,
|
||||
PurgeInterval: 100 * time.Millisecond,
|
||||
})
|
||||
underlyingReads = 0
|
||||
underlyingMisses = 0
|
||||
})
|
||||
|
||||
It("should handle missing secrets correctly and cache misses", func() {
|
||||
secretManager.GetStub = makeGetStub("foo", "value", nil, true, nil, &underlyingReads, &underlyingMisses)
|
||||
|
||||
// miss
|
||||
value, expiration, found, err := cachedSecretManager.Get("bar")
|
||||
Expect(value).To(BeNil())
|
||||
Expect(expiration).To(BeNil())
|
||||
Expect(found).To(BeFalse())
|
||||
Expect(err).To(BeNil())
|
||||
Expect(underlyingReads).To(BeIdenticalTo(0))
|
||||
Expect(underlyingMisses).To(BeIdenticalTo(1))
|
||||
|
||||
// cached miss
|
||||
value, expiration, found, err = cachedSecretManager.Get("bar")
|
||||
Expect(value).To(BeNil())
|
||||
Expect(expiration).To(BeNil())
|
||||
Expect(found).To(BeFalse())
|
||||
Expect(err).To(BeNil())
|
||||
Expect(underlyingReads).To(BeIdenticalTo(0))
|
||||
Expect(underlyingMisses).To(BeIdenticalTo(1))
|
||||
})
|
||||
|
||||
It("should handle existing secrets correctly and cache them, returning previous value if the underlying value has changed", func() {
|
||||
secretManager.GetStub = makeGetStub("foo", "value", nil, true, nil, &underlyingReads, &underlyingMisses)
|
||||
|
||||
// hit
|
||||
value, expiration, found, err := cachedSecretManager.Get("foo")
|
||||
Expect(value).To(BeIdenticalTo("value"))
|
||||
Expect(expiration).To(BeNil())
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
Expect(underlyingReads).To(BeIdenticalTo(1))
|
||||
Expect(underlyingMisses).To(BeIdenticalTo(0))
|
||||
|
||||
// cached hit
|
||||
secretManager.GetStub = makeGetStub("foo", "different-value", nil, true, nil, &underlyingReads, &underlyingMisses)
|
||||
value, expiration, found, err = cachedSecretManager.Get("foo")
|
||||
Expect(value).To(BeIdenticalTo("value"))
|
||||
Expect(expiration).To(BeNil())
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
Expect(underlyingReads).To(BeIdenticalTo(1))
|
||||
Expect(underlyingMisses).To(BeIdenticalTo(0))
|
||||
})
|
||||
|
||||
It("should handle errors correctly and avoid caching errors", func() {
|
||||
secretManager.GetStub = makeGetStub("baz", nil, nil, false, fmt.Errorf("unexpected error"), &underlyingReads, &underlyingMisses)
|
||||
|
||||
// error
|
||||
value, expiration, found, err := cachedSecretManager.Get("baz")
|
||||
Expect(value).To(BeNil())
|
||||
Expect(expiration).To(BeNil())
|
||||
Expect(found).To(BeFalse())
|
||||
Expect(err).NotTo(BeNil())
|
||||
Expect(underlyingReads).To(BeIdenticalTo(1))
|
||||
Expect(underlyingMisses).To(BeIdenticalTo(0))
|
||||
|
||||
// no caching of error
|
||||
value, expiration, found, err = cachedSecretManager.Get("baz")
|
||||
Expect(value).To(BeNil())
|
||||
Expect(expiration).To(BeNil())
|
||||
Expect(found).To(BeFalse())
|
||||
Expect(err).NotTo(BeNil())
|
||||
Expect(underlyingReads).To(BeIdenticalTo(2))
|
||||
Expect(underlyingMisses).To(BeIdenticalTo(0))
|
||||
})
|
||||
|
||||
It("should re-retrieve expired entries", func() {
|
||||
secretManager.GetStub = makeGetStub("foo", "value", nil, true, nil, &underlyingReads, &underlyingMisses)
|
||||
|
||||
// get few entries first
|
||||
_, _, _, _ = cachedSecretManager.Get("foo")
|
||||
_, _, _, _ = cachedSecretManager.Get("bar")
|
||||
_, _, _, _ = cachedSecretManager.Get("baz")
|
||||
Expect(underlyingReads).To(BeIdenticalTo(1))
|
||||
Expect(underlyingMisses).To(BeIdenticalTo(2))
|
||||
|
||||
// get these entries again and make sure they are cached
|
||||
_, _, _, _ = cachedSecretManager.Get("foo")
|
||||
_, _, _, _ = cachedSecretManager.Get("bar")
|
||||
_, _, _, _ = cachedSecretManager.Get("baz")
|
||||
Expect(underlyingReads).To(BeIdenticalTo(1))
|
||||
Expect(underlyingMisses).To(BeIdenticalTo(2))
|
||||
|
||||
// sleep
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
// check counters again and make sure the entries are re-retrieved
|
||||
_, _, _, _ = cachedSecretManager.Get("foo")
|
||||
_, _, _, _ = cachedSecretManager.Get("bar")
|
||||
_, _, _, _ = cachedSecretManager.Get("baz")
|
||||
Expect(underlyingReads).To(BeIdenticalTo(2))
|
||||
Expect(underlyingMisses).To(BeIdenticalTo(4))
|
||||
})
|
||||
|
||||
})
|
|
@ -1,50 +1,48 @@
|
|||
package credhub
|
||||
|
||||
import (
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"code.cloudfoundry.org/credhub-cli/credhub"
|
||||
"code.cloudfoundry.org/credhub-cli/credhub/credentials"
|
||||
"code.cloudfoundry.org/lager"
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
)
|
||||
|
||||
type CredHubAtc struct {
|
||||
CredHub *LazyCredhub
|
||||
logger lager.Logger
|
||||
|
||||
PathPrefix string
|
||||
TeamName string
|
||||
PipelineName string
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (c CredHubAtc) Get(varDef template.VariableDefinition) (interface{}, bool, error) {
|
||||
// NewSecretLookupPaths defines how variables will be searched in the underlying secret manager
|
||||
func (c CredHubAtc) NewSecretLookupPaths(teamName string, pipelineName string) []creds.SecretLookupPath {
|
||||
lookupPaths := []creds.SecretLookupPath{}
|
||||
if len(pipelineName) > 0 {
|
||||
lookupPaths = append(lookupPaths, creds.NewSecretLookupWithPrefix(path.Join(c.prefix, teamName, pipelineName)+"/"))
|
||||
}
|
||||
lookupPaths = append(lookupPaths, creds.NewSecretLookupWithPrefix(path.Join(c.prefix, teamName)+"/"))
|
||||
return lookupPaths
|
||||
}
|
||||
|
||||
// Get retrieves the value and expiration of an individual secret
|
||||
func (c CredHubAtc) Get(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
var cred credentials.Credential
|
||||
var found bool
|
||||
var err error
|
||||
|
||||
if c.PipelineName != "" {
|
||||
path := c.path(c.TeamName, c.PipelineName, varDef.Name)
|
||||
cred, found, err = c.findCred(path)
|
||||
if err != nil {
|
||||
c.logger.Error("could not find cred", err)
|
||||
return nil, false, err
|
||||
}
|
||||
cred, found, err = c.findCred(secretPath)
|
||||
if err != nil {
|
||||
c.logger.Error("unable to retrieve credhub secret", err)
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
if !found {
|
||||
cred, found, err = c.findCred(c.path(c.TeamName, varDef.Name))
|
||||
if err != nil {
|
||||
c.logger.Error("could not find cred", err)
|
||||
return nil, false, err
|
||||
}
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
var result interface{} = cred.Value
|
||||
var result = cred.Value
|
||||
|
||||
if standardMap, ok := cred.Value.(map[string]interface{}); ok {
|
||||
// TODO - we should do this recursively since the cpp4life go-path library
|
||||
|
@ -59,7 +57,7 @@ func (c CredHubAtc) Get(varDef template.VariableDefinition) (interface{}, bool,
|
|||
result = evenLessTyped
|
||||
}
|
||||
|
||||
return result, true, nil
|
||||
return result, nil, true, nil
|
||||
}
|
||||
|
||||
func (c CredHubAtc) findCred(path string) (credentials.Credential, bool, error) {
|
||||
|
@ -87,14 +85,3 @@ func (c CredHubAtc) findCred(path string) (credentials.Credential, bool, error)
|
|||
|
||||
return cred, true, nil
|
||||
}
|
||||
|
||||
func (c CredHubAtc) path(segments ...string) string {
|
||||
return path.Join(append([]string{c.PathPrefix}, segments...)...)
|
||||
}
|
||||
|
||||
func (c CredHubAtc) List() ([]template.VariableDefinition, error) {
|
||||
// not implemented, see vault implementation
|
||||
return []template.VariableDefinition{}, nil
|
||||
}
|
||||
|
||||
var _ template.Variables = new(CredHubAtc)
|
||||
|
|
|
@ -2,7 +2,6 @@ package credhub
|
|||
|
||||
import (
|
||||
"code.cloudfoundry.org/lager"
|
||||
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
)
|
||||
|
||||
|
@ -20,12 +19,10 @@ func NewCredHubFactory(logger lager.Logger, credhub *LazyCredhub, prefix string)
|
|||
}
|
||||
}
|
||||
|
||||
func (factory *credhubFactory) NewVariables(teamName string, pipelineName string) creds.Variables {
|
||||
func (factory *credhubFactory) NewSecrets() creds.Secrets {
|
||||
return &CredHubAtc{
|
||||
CredHub: factory.credhub,
|
||||
PathPrefix: factory.prefix,
|
||||
TeamName: teamName,
|
||||
logger: factory.logger,
|
||||
PipelineName: pipelineName,
|
||||
CredHub: factory.credhub,
|
||||
logger: factory.logger,
|
||||
prefix: factory.prefix,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ func (manager CredHubManager) Health() (*creds.HealthResponse, error) {
|
|||
return healthResponse, nil
|
||||
}
|
||||
|
||||
func (manager CredHubManager) NewVariablesFactory(logger lager.Logger) (creds.VariablesFactory, error) {
|
||||
func (manager CredHubManager) NewSecretsFactory(logger lager.Logger) (creds.SecretsFactory, error) {
|
||||
return NewCredHubFactory(logger, manager.Client, manager.PathPrefix), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package credsfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
)
|
||||
|
||||
type FakeSecrets struct {
|
||||
GetStub func(string) (interface{}, *time.Time, bool, error)
|
||||
getMutex sync.RWMutex
|
||||
getArgsForCall []struct {
|
||||
arg1 string
|
||||
}
|
||||
getReturns struct {
|
||||
result1 interface{}
|
||||
result2 *time.Time
|
||||
result3 bool
|
||||
result4 error
|
||||
}
|
||||
getReturnsOnCall map[int]struct {
|
||||
result1 interface{}
|
||||
result2 *time.Time
|
||||
result3 bool
|
||||
result4 error
|
||||
}
|
||||
NewSecretLookupPathsStub func(string, string) []creds.SecretLookupPath
|
||||
newSecretLookupPathsMutex sync.RWMutex
|
||||
newSecretLookupPathsArgsForCall []struct {
|
||||
arg1 string
|
||||
arg2 string
|
||||
}
|
||||
newSecretLookupPathsReturns struct {
|
||||
result1 []creds.SecretLookupPath
|
||||
}
|
||||
newSecretLookupPathsReturnsOnCall map[int]struct {
|
||||
result1 []creds.SecretLookupPath
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) Get(arg1 string) (interface{}, *time.Time, bool, error) {
|
||||
fake.getMutex.Lock()
|
||||
ret, specificReturn := fake.getReturnsOnCall[len(fake.getArgsForCall)]
|
||||
fake.getArgsForCall = append(fake.getArgsForCall, struct {
|
||||
arg1 string
|
||||
}{arg1})
|
||||
fake.recordInvocation("Get", []interface{}{arg1})
|
||||
fake.getMutex.Unlock()
|
||||
if fake.GetStub != nil {
|
||||
return fake.GetStub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2, ret.result3, ret.result4
|
||||
}
|
||||
fakeReturns := fake.getReturns
|
||||
return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3, fakeReturns.result4
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) GetCallCount() int {
|
||||
fake.getMutex.RLock()
|
||||
defer fake.getMutex.RUnlock()
|
||||
return len(fake.getArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) GetCalls(stub func(string) (interface{}, *time.Time, bool, error)) {
|
||||
fake.getMutex.Lock()
|
||||
defer fake.getMutex.Unlock()
|
||||
fake.GetStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) GetArgsForCall(i int) string {
|
||||
fake.getMutex.RLock()
|
||||
defer fake.getMutex.RUnlock()
|
||||
argsForCall := fake.getArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) GetReturns(result1 interface{}, result2 *time.Time, result3 bool, result4 error) {
|
||||
fake.getMutex.Lock()
|
||||
defer fake.getMutex.Unlock()
|
||||
fake.GetStub = nil
|
||||
fake.getReturns = struct {
|
||||
result1 interface{}
|
||||
result2 *time.Time
|
||||
result3 bool
|
||||
result4 error
|
||||
}{result1, result2, result3, result4}
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) GetReturnsOnCall(i int, result1 interface{}, result2 *time.Time, result3 bool, result4 error) {
|
||||
fake.getMutex.Lock()
|
||||
defer fake.getMutex.Unlock()
|
||||
fake.GetStub = nil
|
||||
if fake.getReturnsOnCall == nil {
|
||||
fake.getReturnsOnCall = make(map[int]struct {
|
||||
result1 interface{}
|
||||
result2 *time.Time
|
||||
result3 bool
|
||||
result4 error
|
||||
})
|
||||
}
|
||||
fake.getReturnsOnCall[i] = struct {
|
||||
result1 interface{}
|
||||
result2 *time.Time
|
||||
result3 bool
|
||||
result4 error
|
||||
}{result1, result2, result3, result4}
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) NewSecretLookupPaths(arg1 string, arg2 string) []creds.SecretLookupPath {
|
||||
fake.newSecretLookupPathsMutex.Lock()
|
||||
ret, specificReturn := fake.newSecretLookupPathsReturnsOnCall[len(fake.newSecretLookupPathsArgsForCall)]
|
||||
fake.newSecretLookupPathsArgsForCall = append(fake.newSecretLookupPathsArgsForCall, struct {
|
||||
arg1 string
|
||||
arg2 string
|
||||
}{arg1, arg2})
|
||||
fake.recordInvocation("NewSecretLookupPaths", []interface{}{arg1, arg2})
|
||||
fake.newSecretLookupPathsMutex.Unlock()
|
||||
if fake.NewSecretLookupPathsStub != nil {
|
||||
return fake.NewSecretLookupPathsStub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.newSecretLookupPathsReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) NewSecretLookupPathsCallCount() int {
|
||||
fake.newSecretLookupPathsMutex.RLock()
|
||||
defer fake.newSecretLookupPathsMutex.RUnlock()
|
||||
return len(fake.newSecretLookupPathsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) NewSecretLookupPathsCalls(stub func(string, string) []creds.SecretLookupPath) {
|
||||
fake.newSecretLookupPathsMutex.Lock()
|
||||
defer fake.newSecretLookupPathsMutex.Unlock()
|
||||
fake.NewSecretLookupPathsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) NewSecretLookupPathsArgsForCall(i int) (string, string) {
|
||||
fake.newSecretLookupPathsMutex.RLock()
|
||||
defer fake.newSecretLookupPathsMutex.RUnlock()
|
||||
argsForCall := fake.newSecretLookupPathsArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) NewSecretLookupPathsReturns(result1 []creds.SecretLookupPath) {
|
||||
fake.newSecretLookupPathsMutex.Lock()
|
||||
defer fake.newSecretLookupPathsMutex.Unlock()
|
||||
fake.NewSecretLookupPathsStub = nil
|
||||
fake.newSecretLookupPathsReturns = struct {
|
||||
result1 []creds.SecretLookupPath
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) NewSecretLookupPathsReturnsOnCall(i int, result1 []creds.SecretLookupPath) {
|
||||
fake.newSecretLookupPathsMutex.Lock()
|
||||
defer fake.newSecretLookupPathsMutex.Unlock()
|
||||
fake.NewSecretLookupPathsStub = nil
|
||||
if fake.newSecretLookupPathsReturnsOnCall == nil {
|
||||
fake.newSecretLookupPathsReturnsOnCall = make(map[int]struct {
|
||||
result1 []creds.SecretLookupPath
|
||||
})
|
||||
}
|
||||
fake.newSecretLookupPathsReturnsOnCall[i] = struct {
|
||||
result1 []creds.SecretLookupPath
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.getMutex.RLock()
|
||||
defer fake.getMutex.RUnlock()
|
||||
fake.newSecretLookupPathsMutex.RLock()
|
||||
defer fake.newSecretLookupPathsMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeSecrets) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ creds.Secrets = new(FakeSecrets)
|
|
@ -0,0 +1,101 @@
|
|||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package credsfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
)
|
||||
|
||||
type FakeSecretsFactory struct {
|
||||
NewSecretsStub func() creds.Secrets
|
||||
newSecretsMutex sync.RWMutex
|
||||
newSecretsArgsForCall []struct {
|
||||
}
|
||||
newSecretsReturns struct {
|
||||
result1 creds.Secrets
|
||||
}
|
||||
newSecretsReturnsOnCall map[int]struct {
|
||||
result1 creds.Secrets
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeSecretsFactory) NewSecrets() creds.Secrets {
|
||||
fake.newSecretsMutex.Lock()
|
||||
ret, specificReturn := fake.newSecretsReturnsOnCall[len(fake.newSecretsArgsForCall)]
|
||||
fake.newSecretsArgsForCall = append(fake.newSecretsArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("NewSecrets", []interface{}{})
|
||||
fake.newSecretsMutex.Unlock()
|
||||
if fake.NewSecretsStub != nil {
|
||||
return fake.NewSecretsStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.newSecretsReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeSecretsFactory) NewSecretsCallCount() int {
|
||||
fake.newSecretsMutex.RLock()
|
||||
defer fake.newSecretsMutex.RUnlock()
|
||||
return len(fake.newSecretsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeSecretsFactory) NewSecretsCalls(stub func() creds.Secrets) {
|
||||
fake.newSecretsMutex.Lock()
|
||||
defer fake.newSecretsMutex.Unlock()
|
||||
fake.NewSecretsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeSecretsFactory) NewSecretsReturns(result1 creds.Secrets) {
|
||||
fake.newSecretsMutex.Lock()
|
||||
defer fake.newSecretsMutex.Unlock()
|
||||
fake.NewSecretsStub = nil
|
||||
fake.newSecretsReturns = struct {
|
||||
result1 creds.Secrets
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeSecretsFactory) NewSecretsReturnsOnCall(i int, result1 creds.Secrets) {
|
||||
fake.newSecretsMutex.Lock()
|
||||
defer fake.newSecretsMutex.Unlock()
|
||||
fake.NewSecretsStub = nil
|
||||
if fake.newSecretsReturnsOnCall == nil {
|
||||
fake.newSecretsReturnsOnCall = make(map[int]struct {
|
||||
result1 creds.Secrets
|
||||
})
|
||||
}
|
||||
fake.newSecretsReturnsOnCall[i] = struct {
|
||||
result1 creds.Secrets
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeSecretsFactory) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.newSecretsMutex.RLock()
|
||||
defer fake.newSecretsMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeSecretsFactory) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ creds.SecretsFactory = new(FakeSecretsFactory)
|
|
@ -1,190 +0,0 @@
|
|||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package credsfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
)
|
||||
|
||||
type FakeVariables struct {
|
||||
GetStub func(template.VariableDefinition) (interface{}, bool, error)
|
||||
getMutex sync.RWMutex
|
||||
getArgsForCall []struct {
|
||||
arg1 template.VariableDefinition
|
||||
}
|
||||
getReturns struct {
|
||||
result1 interface{}
|
||||
result2 bool
|
||||
result3 error
|
||||
}
|
||||
getReturnsOnCall map[int]struct {
|
||||
result1 interface{}
|
||||
result2 bool
|
||||
result3 error
|
||||
}
|
||||
ListStub func() ([]template.VariableDefinition, error)
|
||||
listMutex sync.RWMutex
|
||||
listArgsForCall []struct {
|
||||
}
|
||||
listReturns struct {
|
||||
result1 []template.VariableDefinition
|
||||
result2 error
|
||||
}
|
||||
listReturnsOnCall map[int]struct {
|
||||
result1 []template.VariableDefinition
|
||||
result2 error
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) Get(arg1 template.VariableDefinition) (interface{}, bool, error) {
|
||||
fake.getMutex.Lock()
|
||||
ret, specificReturn := fake.getReturnsOnCall[len(fake.getArgsForCall)]
|
||||
fake.getArgsForCall = append(fake.getArgsForCall, struct {
|
||||
arg1 template.VariableDefinition
|
||||
}{arg1})
|
||||
fake.recordInvocation("Get", []interface{}{arg1})
|
||||
fake.getMutex.Unlock()
|
||||
if fake.GetStub != nil {
|
||||
return fake.GetStub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2, ret.result3
|
||||
}
|
||||
fakeReturns := fake.getReturns
|
||||
return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) GetCallCount() int {
|
||||
fake.getMutex.RLock()
|
||||
defer fake.getMutex.RUnlock()
|
||||
return len(fake.getArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) GetCalls(stub func(template.VariableDefinition) (interface{}, bool, error)) {
|
||||
fake.getMutex.Lock()
|
||||
defer fake.getMutex.Unlock()
|
||||
fake.GetStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) GetArgsForCall(i int) template.VariableDefinition {
|
||||
fake.getMutex.RLock()
|
||||
defer fake.getMutex.RUnlock()
|
||||
argsForCall := fake.getArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) GetReturns(result1 interface{}, result2 bool, result3 error) {
|
||||
fake.getMutex.Lock()
|
||||
defer fake.getMutex.Unlock()
|
||||
fake.GetStub = nil
|
||||
fake.getReturns = struct {
|
||||
result1 interface{}
|
||||
result2 bool
|
||||
result3 error
|
||||
}{result1, result2, result3}
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) GetReturnsOnCall(i int, result1 interface{}, result2 bool, result3 error) {
|
||||
fake.getMutex.Lock()
|
||||
defer fake.getMutex.Unlock()
|
||||
fake.GetStub = nil
|
||||
if fake.getReturnsOnCall == nil {
|
||||
fake.getReturnsOnCall = make(map[int]struct {
|
||||
result1 interface{}
|
||||
result2 bool
|
||||
result3 error
|
||||
})
|
||||
}
|
||||
fake.getReturnsOnCall[i] = struct {
|
||||
result1 interface{}
|
||||
result2 bool
|
||||
result3 error
|
||||
}{result1, result2, result3}
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) List() ([]template.VariableDefinition, error) {
|
||||
fake.listMutex.Lock()
|
||||
ret, specificReturn := fake.listReturnsOnCall[len(fake.listArgsForCall)]
|
||||
fake.listArgsForCall = append(fake.listArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("List", []interface{}{})
|
||||
fake.listMutex.Unlock()
|
||||
if fake.ListStub != nil {
|
||||
return fake.ListStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
fakeReturns := fake.listReturns
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) ListCallCount() int {
|
||||
fake.listMutex.RLock()
|
||||
defer fake.listMutex.RUnlock()
|
||||
return len(fake.listArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) ListCalls(stub func() ([]template.VariableDefinition, error)) {
|
||||
fake.listMutex.Lock()
|
||||
defer fake.listMutex.Unlock()
|
||||
fake.ListStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) ListReturns(result1 []template.VariableDefinition, result2 error) {
|
||||
fake.listMutex.Lock()
|
||||
defer fake.listMutex.Unlock()
|
||||
fake.ListStub = nil
|
||||
fake.listReturns = struct {
|
||||
result1 []template.VariableDefinition
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) ListReturnsOnCall(i int, result1 []template.VariableDefinition, result2 error) {
|
||||
fake.listMutex.Lock()
|
||||
defer fake.listMutex.Unlock()
|
||||
fake.ListStub = nil
|
||||
if fake.listReturnsOnCall == nil {
|
||||
fake.listReturnsOnCall = make(map[int]struct {
|
||||
result1 []template.VariableDefinition
|
||||
result2 error
|
||||
})
|
||||
}
|
||||
fake.listReturnsOnCall[i] = struct {
|
||||
result1 []template.VariableDefinition
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.getMutex.RLock()
|
||||
defer fake.getMutex.RUnlock()
|
||||
fake.listMutex.RLock()
|
||||
defer fake.listMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeVariables) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ creds.Variables = new(FakeVariables)
|
|
@ -1,112 +0,0 @@
|
|||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package credsfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
)
|
||||
|
||||
type FakeVariablesFactory struct {
|
||||
NewVariablesStub func(string, string) creds.Variables
|
||||
newVariablesMutex sync.RWMutex
|
||||
newVariablesArgsForCall []struct {
|
||||
arg1 string
|
||||
arg2 string
|
||||
}
|
||||
newVariablesReturns struct {
|
||||
result1 creds.Variables
|
||||
}
|
||||
newVariablesReturnsOnCall map[int]struct {
|
||||
result1 creds.Variables
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeVariablesFactory) NewVariables(arg1 string, arg2 string) creds.Variables {
|
||||
fake.newVariablesMutex.Lock()
|
||||
ret, specificReturn := fake.newVariablesReturnsOnCall[len(fake.newVariablesArgsForCall)]
|
||||
fake.newVariablesArgsForCall = append(fake.newVariablesArgsForCall, struct {
|
||||
arg1 string
|
||||
arg2 string
|
||||
}{arg1, arg2})
|
||||
fake.recordInvocation("NewVariables", []interface{}{arg1, arg2})
|
||||
fake.newVariablesMutex.Unlock()
|
||||
if fake.NewVariablesStub != nil {
|
||||
return fake.NewVariablesStub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.newVariablesReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeVariablesFactory) NewVariablesCallCount() int {
|
||||
fake.newVariablesMutex.RLock()
|
||||
defer fake.newVariablesMutex.RUnlock()
|
||||
return len(fake.newVariablesArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeVariablesFactory) NewVariablesCalls(stub func(string, string) creds.Variables) {
|
||||
fake.newVariablesMutex.Lock()
|
||||
defer fake.newVariablesMutex.Unlock()
|
||||
fake.NewVariablesStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeVariablesFactory) NewVariablesArgsForCall(i int) (string, string) {
|
||||
fake.newVariablesMutex.RLock()
|
||||
defer fake.newVariablesMutex.RUnlock()
|
||||
argsForCall := fake.newVariablesArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeVariablesFactory) NewVariablesReturns(result1 creds.Variables) {
|
||||
fake.newVariablesMutex.Lock()
|
||||
defer fake.newVariablesMutex.Unlock()
|
||||
fake.NewVariablesStub = nil
|
||||
fake.newVariablesReturns = struct {
|
||||
result1 creds.Variables
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeVariablesFactory) NewVariablesReturnsOnCall(i int, result1 creds.Variables) {
|
||||
fake.newVariablesMutex.Lock()
|
||||
defer fake.newVariablesMutex.Unlock()
|
||||
fake.NewVariablesStub = nil
|
||||
if fake.newVariablesReturnsOnCall == nil {
|
||||
fake.newVariablesReturnsOnCall = make(map[int]struct {
|
||||
result1 creds.Variables
|
||||
})
|
||||
}
|
||||
fake.newVariablesReturnsOnCall[i] = struct {
|
||||
result1 creds.Variables
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeVariablesFactory) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.newVariablesMutex.RLock()
|
||||
defer fake.newVariablesMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeVariablesFactory) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ creds.VariablesFactory = new(FakeVariablesFactory)
|
|
@ -2,8 +2,12 @@ package kubernetes
|
|||
|
||||
import (
|
||||
"code.cloudfoundry.org/lager"
|
||||
"fmt"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
k8s_errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -12,48 +16,56 @@ import (
|
|||
|
||||
type Kubernetes struct {
|
||||
Clientset *kubernetes.Clientset
|
||||
TeamName string
|
||||
PipelineName string
|
||||
NamespacePrefix string
|
||||
logger lager.Logger
|
||||
namespacePrefix string
|
||||
}
|
||||
|
||||
func (k Kubernetes) Get(varDef template.VariableDefinition) (interface{}, bool, error) {
|
||||
var namespace = k.NamespacePrefix + k.TeamName
|
||||
var pipelineSecretName = k.PipelineName + "." + varDef.Name
|
||||
var secretName = varDef.Name
|
||||
// NewSecretLookupPaths defines how variables will be searched in the underlying secret manager
|
||||
func (k Kubernetes) NewSecretLookupPaths(teamName string, pipelineName string) []creds.SecretLookupPath {
|
||||
lookupPaths := []creds.SecretLookupPath{}
|
||||
if len(pipelineName) > 0 {
|
||||
lookupPaths = append(lookupPaths, creds.NewSecretLookupWithPrefix(path.Join(k.namespacePrefix, teamName, pipelineName)+"/"))
|
||||
}
|
||||
lookupPaths = append(lookupPaths, creds.NewSecretLookupWithPrefix(path.Join(k.namespacePrefix, teamName)+"/"))
|
||||
return lookupPaths
|
||||
}
|
||||
|
||||
secret, found, err := k.findSecret(namespace, pipelineSecretName)
|
||||
|
||||
if !found && err == nil {
|
||||
secret, found, err = k.findSecret(namespace, secretName)
|
||||
// Get retrieves the value and expiration of an individual secret
|
||||
func (k Kubernetes) Get(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
parts := strings.Split(secretPath, ":")
|
||||
if len(parts) != 2 {
|
||||
return nil, nil, false, fmt.Errorf("unable to split kubernetes secret path into [namespace]:[secret]: %s", secretPath)
|
||||
}
|
||||
|
||||
var namespace = parts[0]
|
||||
var secretName = parts[1]
|
||||
|
||||
secret, found, err := k.findSecret(namespace, secretName)
|
||||
|
||||
if err != nil {
|
||||
k.logger.Error("k8s-secret-error", err, lager.Data{
|
||||
"namespace": namespace,
|
||||
"pipelineSecretName": pipelineSecretName,
|
||||
"secretName": secretName,
|
||||
k.logger.Error("unable to retrieve kubernetes secret", err, lager.Data{
|
||||
"namespace": namespace,
|
||||
"secretName": secretName,
|
||||
})
|
||||
return nil, false, err
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
if found {
|
||||
return k.getValueFromSecret(secret)
|
||||
}
|
||||
|
||||
k.logger.Info("k8s-secret-not-found", lager.Data{
|
||||
"namespace": namespace,
|
||||
"pipelineSecretName": pipelineSecretName,
|
||||
"secretName": secretName,
|
||||
k.logger.Info("kubernetes secret not found", lager.Data{
|
||||
"namespace": namespace,
|
||||
"secretName": secretName,
|
||||
})
|
||||
return nil, false, nil
|
||||
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
||||
func (k Kubernetes) getValueFromSecret(secret *v1.Secret) (interface{}, bool, error) {
|
||||
func (k Kubernetes) getValueFromSecret(secret *v1.Secret) (interface{}, *time.Time, bool, error) {
|
||||
val, found := secret.Data["value"]
|
||||
if found {
|
||||
return string(val), true, nil
|
||||
return string(val), nil, true, nil
|
||||
}
|
||||
|
||||
evenLessTyped := map[interface{}]interface{}{}
|
||||
|
@ -61,7 +73,7 @@ func (k Kubernetes) getValueFromSecret(secret *v1.Secret) (interface{}, bool, er
|
|||
evenLessTyped[k] = string(v)
|
||||
}
|
||||
|
||||
return evenLessTyped, true, nil
|
||||
return evenLessTyped, nil, true, nil
|
||||
}
|
||||
|
||||
func (k Kubernetes) findSecret(namespace, name string) (*v1.Secret, bool, error) {
|
||||
|
@ -78,9 +90,3 @@ func (k Kubernetes) findSecret(namespace, name string) (*v1.Secret, bool, error)
|
|||
return secret, true, err
|
||||
}
|
||||
}
|
||||
|
||||
func (k Kubernetes) List() ([]template.VariableDefinition, error) {
|
||||
// Unimplemented for Kubernetes secrets
|
||||
|
||||
return []template.VariableDefinition{}, nil
|
||||
}
|
||||
|
|
|
@ -23,12 +23,20 @@ func NewKubernetesFactory(logger lager.Logger, clientset *kubernetes.Clientset,
|
|||
return factory
|
||||
}
|
||||
|
||||
func (factory *kubernetesFactory) NewVariables(teamName string, pipelineName string) creds.Variables {
|
||||
func (factory *kubernetesFactory) NewSecrets() creds.Secrets {
|
||||
return &Kubernetes{
|
||||
Clientset: factory.clientset,
|
||||
TeamName: teamName,
|
||||
PipelineName: pipelineName,
|
||||
NamespacePrefix: factory.namespacePrefix,
|
||||
logger: factory.logger,
|
||||
namespacePrefix: factory.namespacePrefix,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSecretLookupPaths defines how variables will be searched in the underlying secret manager
|
||||
func (factory *kubernetesFactory) NewSecretLookupPaths(teamName string, pipelineName string) []creds.SecretLookupPath {
|
||||
lookupPaths := []creds.SecretLookupPath{}
|
||||
if len(pipelineName) > 0 {
|
||||
lookupPaths = append(lookupPaths, creds.NewSecretLookupWithPrefix(factory.namespacePrefix+teamName+":"+pipelineName+"."))
|
||||
}
|
||||
lookupPaths = append(lookupPaths, creds.NewSecretLookupWithPrefix(factory.namespacePrefix+teamName+":"))
|
||||
return lookupPaths
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func (manager KubernetesManager) Validate() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (manager KubernetesManager) NewVariablesFactory(logger lager.Logger) (creds.VariablesFactory, error) {
|
||||
func (manager KubernetesManager) NewSecretsFactory(logger lager.Logger) (creds.SecretsFactory, error) {
|
||||
config, err := manager.buildConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -2,7 +2,7 @@ package creds
|
|||
|
||||
import (
|
||||
"code.cloudfoundry.org/lager"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
"github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
|
@ -11,7 +11,7 @@ type Manager interface {
|
|||
Health() (*HealthResponse, error)
|
||||
Init(lager.Logger) error
|
||||
|
||||
NewVariablesFactory(lager.Logger) (VariablesFactory, error)
|
||||
NewSecretsFactory(lager.Logger) (SecretsFactory, error)
|
||||
}
|
||||
|
||||
type ManagerFactory interface {
|
||||
|
@ -22,6 +22,7 @@ type Managers map[string]Manager
|
|||
|
||||
type CredentialManagementConfig struct {
|
||||
RetryConfig SecretRetryConfig
|
||||
CacheConfig SecretCacheConfig
|
||||
}
|
||||
|
||||
type HealthResponse struct {
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package noop
|
||||
|
||||
import "github.com/cloudfoundry/bosh-cli/director/template"
|
||||
import (
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Noop struct{}
|
||||
|
||||
func (n Noop) Get(varDef template.VariableDefinition) (interface{}, bool, error) {
|
||||
return nil, false, nil
|
||||
func (n Noop) NewSecretLookupPaths(string, string) []creds.SecretLookupPath {
|
||||
return []creds.SecretLookupPath{}
|
||||
}
|
||||
|
||||
func (n Noop) List() ([]template.VariableDefinition, error) {
|
||||
return []template.VariableDefinition{}, nil
|
||||
func (n Noop) Get(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
|
|
@ -8,6 +8,6 @@ func NewNoopFactory() *noopFactory {
|
|||
return &noopFactory{}
|
||||
}
|
||||
|
||||
func (*noopFactory) NewVariables(string, string) creds.Variables {
|
||||
func (*noopFactory) NewSecrets() creds.Secrets {
|
||||
return &Noop{}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package noop_test
|
||||
|
||||
import (
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
. "github.com/concourse/concourse/atc/creds/noop"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
@ -17,15 +17,17 @@ var _ = Describe("Noop", func() {
|
|||
|
||||
Describe("Get", func() {
|
||||
var val interface{}
|
||||
var expiration *time.Time
|
||||
var found bool
|
||||
var getErr error
|
||||
|
||||
JustBeforeEach(func() {
|
||||
val, found, getErr = noop.Get(template.VariableDefinition{})
|
||||
val, expiration, found, getErr = noop.Get("foo")
|
||||
})
|
||||
|
||||
It("never locates the variable", func() {
|
||||
Expect(val).To(BeNil())
|
||||
Expect(expiration).To(BeNil())
|
||||
Expect(found).To(BeFalse())
|
||||
Expect(getErr).ToNot(HaveOccurred())
|
||||
})
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package creds
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/concourse/retryhttp"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SecretRetryConfig struct {
|
||||
Attempts int `long:"secret-retry-attempts" default:"5" description:"The number of attempts secret will be retried to be fetched, in case a retryable error happens."`
|
||||
Interval time.Duration `long:"secret-retry-interval" default:"1s" description:"The interval between secret retry retrieval attempts."`
|
||||
}
|
||||
|
||||
type RetryableSecrets struct {
|
||||
secrets Secrets
|
||||
retryConfig SecretRetryConfig
|
||||
}
|
||||
|
||||
func NewRetryableSecrets(secrets Secrets, retryConfig SecretRetryConfig) Secrets {
|
||||
return &RetryableSecrets{secrets: secrets, retryConfig: retryConfig}
|
||||
}
|
||||
|
||||
// Get retrieves the value and expiration of an individual secret
|
||||
func (rs RetryableSecrets) Get(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
r := &retryhttp.DefaultRetryer{}
|
||||
for i := 0; i < rs.retryConfig.Attempts-1; i++ {
|
||||
result, expiration, exists, err := rs.secrets.Get(secretPath)
|
||||
if err != nil && r.IsRetryable(err) {
|
||||
time.Sleep(rs.retryConfig.Interval)
|
||||
continue
|
||||
}
|
||||
return result, expiration, exists, err
|
||||
}
|
||||
result, expiration, exists, err := rs.secrets.Get(secretPath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s (after %d retries)", err, rs.retryConfig.Attempts)
|
||||
}
|
||||
return result, expiration, exists, err
|
||||
}
|
||||
|
||||
// NewSecretLookupPaths defines how variables will be searched in the underlying secret manager
|
||||
func (rs RetryableSecrets) NewSecretLookupPaths(teamName string, pipelineName string) []SecretLookupPath {
|
||||
return rs.secrets.NewSecretLookupPaths(teamName, pipelineName)
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package creds_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"github.com/concourse/concourse/atc/creds/credsfakes"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"time"
|
||||
)
|
||||
|
||||
func makeFlakySecretManager(numberOfFails int) creds.Secrets {
|
||||
fakeSecretManager := new(credsfakes.FakeSecrets)
|
||||
attempt := 0
|
||||
fakeSecretManager.GetStub = func(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
attempt++
|
||||
if attempt <= numberOfFails {
|
||||
return nil, nil, false, fmt.Errorf("remote error: handshake failure")
|
||||
}
|
||||
return "received value", nil, true, nil
|
||||
}
|
||||
return fakeSecretManager
|
||||
}
|
||||
|
||||
var _ = Describe("Re-retrieval of secrets on retryable errors", func() {
|
||||
|
||||
It("should retry receiving a parameter in case of retryable error", func() {
|
||||
flakySecretManager := makeFlakySecretManager(3)
|
||||
retryableSecretManager := creds.NewRetryableSecrets(flakySecretManager, creds.SecretRetryConfig{Attempts: 5, Interval: time.Millisecond})
|
||||
varDef := template.VariableDefinition{Name: "somevar"}
|
||||
value, found, err := creds.NewVariables(retryableSecretManager, "team", "pipeline").Get(varDef)
|
||||
Expect(value).To(BeEquivalentTo("received value"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
|
||||
It("should not receive a parameter if the number of retryable errors exceeded the number of allowed attempts", func() {
|
||||
flakySecretManager := makeFlakySecretManager(10)
|
||||
retryableSecretManager := creds.NewRetryableSecrets(flakySecretManager, creds.SecretRetryConfig{Attempts: 5, Interval: time.Millisecond})
|
||||
varDef := template.VariableDefinition{Name: "somevar"}
|
||||
value, found, err := creds.NewVariables(retryableSecretManager, "team", "pipeline").Get(varDef)
|
||||
Expect(value).To(BeNil())
|
||||
Expect(found).To(BeFalse())
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
|
||||
})
|
|
@ -1,65 +0,0 @@
|
|||
package creds
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
"github.com/concourse/retryhttp"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SecretRetryConfig struct {
|
||||
Attempts int `long:"secret-retry-attempts" default:"5" description:"The number of attempts secret will be retried to be fetched, in case a retryable error happens."`
|
||||
Interval time.Duration `long:"secret-retry-interval" default:"1s" description:"The interval between secret retry retrieval attempts."`
|
||||
}
|
||||
|
||||
type RetryableVariablesFactory struct {
|
||||
factory VariablesFactory
|
||||
retryConfig SecretRetryConfig
|
||||
}
|
||||
|
||||
type RetryableVariables struct {
|
||||
variables Variables
|
||||
retryConfig SecretRetryConfig
|
||||
}
|
||||
|
||||
func NewRetryableVariablesFactory(factory VariablesFactory, retryConfig SecretRetryConfig) VariablesFactory {
|
||||
return &RetryableVariablesFactory{factory: factory, retryConfig: retryConfig}
|
||||
}
|
||||
|
||||
func (rvf RetryableVariablesFactory) NewVariables(teamName string, pipelineName string) Variables {
|
||||
return RetryableVariables{variables: rvf.factory.NewVariables(teamName, pipelineName), retryConfig: rvf.retryConfig}
|
||||
}
|
||||
|
||||
func (rv RetryableVariables) Get(varDef template.VariableDefinition) (interface{}, bool, error) {
|
||||
r := &retryhttp.DefaultRetryer{}
|
||||
for i := 0; i < rv.retryConfig.Attempts-1; i++ {
|
||||
result, exists, err := rv.variables.Get(varDef)
|
||||
if err != nil && r.IsRetryable(err) {
|
||||
time.Sleep(rv.retryConfig.Interval)
|
||||
continue
|
||||
}
|
||||
return result, exists, err
|
||||
}
|
||||
result, exists, err := rv.variables.Get(varDef)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s (after %d retries)", err, rv.retryConfig.Attempts)
|
||||
}
|
||||
return result, exists, err
|
||||
}
|
||||
|
||||
func (rv RetryableVariables) List() ([]template.VariableDefinition, error) {
|
||||
r := &retryhttp.DefaultRetryer{}
|
||||
for i := 0; i < rv.retryConfig.Attempts-1; i++ {
|
||||
result, err := rv.variables.List()
|
||||
if err != nil && r.IsRetryable(err) {
|
||||
time.Sleep(rv.retryConfig.Interval)
|
||||
continue
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
result, err := rv.variables.List()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s (after %d retries)", err, rv.retryConfig.Attempts)
|
||||
}
|
||||
return result, err
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package creds_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"time"
|
||||
)
|
||||
|
||||
type flakyVariables struct {
|
||||
attempt int
|
||||
numberOfFails int
|
||||
}
|
||||
|
||||
func (f *flakyVariables) Get(template.VariableDefinition) (interface{}, bool, error) {
|
||||
f.attempt++
|
||||
if f.attempt <= f.numberOfFails {
|
||||
return nil, false, fmt.Errorf("remote error: handshake failure")
|
||||
}
|
||||
return "received value", true, nil
|
||||
}
|
||||
|
||||
func (f *flakyVariables) List() ([]template.VariableDefinition, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type flakyFactory struct {
|
||||
flakyVariables creds.Variables
|
||||
}
|
||||
|
||||
func (f *flakyFactory) NewVariables(string, string) creds.Variables {
|
||||
return f.flakyVariables
|
||||
}
|
||||
|
||||
var _ = Describe("Retryable Variables Factory", func() {
|
||||
|
||||
It("should retry receiving a parameter in case of retryable error", func() {
|
||||
flakyFactory := &flakyFactory{flakyVariables: &flakyVariables{numberOfFails: 3}}
|
||||
factory := creds.NewRetryableVariablesFactory(flakyFactory, creds.SecretRetryConfig{Attempts: 5, Interval: time.Millisecond})
|
||||
varDef := template.VariableDefinition{Name: "somevar"}
|
||||
value, found, err := factory.NewVariables("team", "pipeline").Get(varDef)
|
||||
Expect(value).To(BeEquivalentTo("received value"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
|
||||
It("should not receive a parameter if the number of retryable errors exceeded the number of allowed attempts", func() {
|
||||
flakyFactory := &flakyFactory{flakyVariables: &flakyVariables{numberOfFails: 10}}
|
||||
factory := creds.NewRetryableVariablesFactory(flakyFactory, creds.SecretRetryConfig{Attempts: 5, Interval: time.Millisecond})
|
||||
varDef := template.VariableDefinition{Name: "somevar"}
|
||||
value, found, err := factory.NewVariables("team", "pipeline").Get(varDef)
|
||||
Expect(value).To(BeNil())
|
||||
Expect(found).To(BeFalse())
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
|
||||
})
|
|
@ -0,0 +1,46 @@
|
|||
package creds
|
||||
|
||||
import (
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
)
|
||||
|
||||
type VariableLookupFromSecrets struct {
|
||||
Secrets Secrets
|
||||
LookupPaths []SecretLookupPath
|
||||
}
|
||||
|
||||
func NewVariables(secrets Secrets, teamName string, pipelineName string) template.Variables {
|
||||
return VariableLookupFromSecrets{
|
||||
Secrets: secrets,
|
||||
LookupPaths: secrets.NewSecretLookupPaths(teamName, pipelineName),
|
||||
}
|
||||
}
|
||||
|
||||
func (sl VariableLookupFromSecrets) Get(varDef template.VariableDefinition) (interface{}, bool, error) {
|
||||
// try to find a secret according to our var->secret lookup paths
|
||||
if len(sl.LookupPaths) > 0 {
|
||||
for _, rule := range sl.LookupPaths {
|
||||
secretId, err := rule.VariableToSecretPath(varDef.Name)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
result, _, found, err := sl.Secrets.Get(secretId)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
return result, true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
} else {
|
||||
// if no paths are specified (i.e. for fake & noop secret managers), then try 1-to-1 var->secret mapping
|
||||
result, _, found, err := sl.Secrets.Get(varDef.Name)
|
||||
return result, found, err
|
||||
}
|
||||
}
|
||||
|
||||
func (sl VariableLookupFromSecrets) List() ([]template.VariableDefinition, error) {
|
||||
return nil, nil
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package creds
|
||||
|
||||
// SecretLookupPath transforms variable name into full secret path
|
||||
type SecretLookupPath interface {
|
||||
VariableToSecretPath(string) (string, error)
|
||||
}
|
||||
|
||||
// SecretLookupWithPrefix is an implementation which returns [prefix][separator][varName]
|
||||
type SecretLookupWithPrefix struct {
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func NewSecretLookupWithPrefix(prefix string) SecretLookupPath {
|
||||
return &SecretLookupWithPrefix{
|
||||
Prefix: prefix,
|
||||
}
|
||||
}
|
||||
|
||||
func (sl SecretLookupWithPrefix) VariableToSecretPath(varName string) (string, error) {
|
||||
return sl.Prefix + varName, nil
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package creds
|
||||
|
||||
import (
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate counterfeiter . SecretsFactory
|
||||
|
||||
type SecretsFactory interface {
|
||||
// NewSecrets returns an instance of a secret manager, capable of retrieving individual secrets
|
||||
NewSecrets() Secrets
|
||||
}
|
||||
|
||||
//go:generate counterfeiter . Secrets
|
||||
|
||||
type Secrets interface {
|
||||
// Every credential manager needs to be able to return (secret, secret_expiration_time, exists, error) based on the secret path
|
||||
Get(string) (interface{}, *time.Time, bool, error)
|
||||
|
||||
// NewSecretLookupPaths returns an instance of lookup policy, which can transform pipeline ((var)) into one or more secret paths, based on team name and pipeline name
|
||||
NewSecretLookupPaths(string, string) []SecretLookupPath
|
||||
}
|
||||
|
||||
type Variables = template.Variables
|
|
@ -69,7 +69,7 @@ func (manager *Manager) Health() (*creds.HealthResponse, error) {
|
|||
Method: "GetSecretValue",
|
||||
}
|
||||
|
||||
_, _, err := manager.SecretManager.getSecretById("__concourse-health-check")
|
||||
_, _, _, err := manager.SecretManager.getSecretById("__concourse-health-check")
|
||||
if err != nil {
|
||||
health.Error = err.Error()
|
||||
return health, nil
|
||||
|
@ -137,7 +137,7 @@ func (manager *Manager) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (manager *Manager) NewVariablesFactory(log lager.Logger) (creds.VariablesFactory, error) {
|
||||
func (manager *Manager) NewSecretsFactory(log lager.Logger) (creds.SecretsFactory, error) {
|
||||
config := &aws.Config{Region: &manager.AwsRegion}
|
||||
if manager.AwsAccessKeyID != "" {
|
||||
config.Credentials = credentials.NewStaticCredentials(manager.AwsAccessKeyID, manager.AwsSecretAccessKey, manager.AwsSessionToken)
|
||||
|
|
|
@ -3,69 +3,60 @@ package secretsmanager
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"code.cloudfoundry.org/lager"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/secretsmanager"
|
||||
"github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface"
|
||||
|
||||
varTemplate "github.com/cloudfoundry/bosh-cli/director/template"
|
||||
)
|
||||
|
||||
type SecretsManager struct {
|
||||
log lager.Logger
|
||||
api secretsmanageriface.SecretsManagerAPI
|
||||
TeamName string
|
||||
PipelineName string
|
||||
SecretTemplates []*template.Template
|
||||
secretTemplates []*template.Template
|
||||
}
|
||||
|
||||
func NewSecretsManager(log lager.Logger, api secretsmanageriface.SecretsManagerAPI, teamName string, pipelineName string, secretTemplates []*template.Template) *SecretsManager {
|
||||
func NewSecretsManager(log lager.Logger, api secretsmanageriface.SecretsManagerAPI, secretTemplates []*template.Template) *SecretsManager {
|
||||
return &SecretsManager{
|
||||
log: log,
|
||||
api: api,
|
||||
TeamName: teamName,
|
||||
PipelineName: pipelineName,
|
||||
SecretTemplates: secretTemplates,
|
||||
secretTemplates: secretTemplates,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SecretsManager) buildSecretId(nameTemplate *template.Template, secret string) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
err := nameTemplate.Execute(&buf, &Secret{
|
||||
Team: s.TeamName,
|
||||
Pipeline: s.PipelineName,
|
||||
Secret: secret,
|
||||
})
|
||||
return buf.String(), err
|
||||
}
|
||||
// NewSecretLookupPaths defines how variables will be searched in the underlying secret manager
|
||||
func (s *SecretsManager) NewSecretLookupPaths(teamName string, pipelineName string) []creds.SecretLookupPath {
|
||||
lookupPaths := []creds.SecretLookupPath{}
|
||||
for _, tmpl := range s.secretTemplates {
|
||||
lPath := NewSecretLookupPathAws(tmpl, teamName, pipelineName)
|
||||
|
||||
func (s *SecretsManager) Get(varDef varTemplate.VariableDefinition) (interface{}, bool, error) {
|
||||
for _, st := range s.SecretTemplates {
|
||||
secretId, err := s.buildSecretId(st, varDef.Name)
|
||||
if err != nil {
|
||||
s.log.Error("build-secret-id", err, lager.Data{"template": st.Name(), "secret": varDef.Name})
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if strings.Contains(secretId, "//") {
|
||||
continue
|
||||
}
|
||||
|
||||
value, found, err := s.getSecretById(secretId)
|
||||
if err != nil {
|
||||
s.log.Error("get-secret", err, lager.Data{
|
||||
"template": st.Name(), "secret": varDef.Name, "secretId": secretId,
|
||||
})
|
||||
return nil, false, err
|
||||
}
|
||||
if found {
|
||||
return value, true, nil
|
||||
// if pipeline name is empty, double slashes may be present in the rendered template
|
||||
// let's avoid adding these templates
|
||||
samplePath, err := lPath.VariableToSecretPath("variable")
|
||||
if err == nil && !strings.Contains(samplePath, "//") {
|
||||
lookupPaths = append(lookupPaths, lPath)
|
||||
}
|
||||
}
|
||||
return nil, false, nil
|
||||
return lookupPaths
|
||||
}
|
||||
|
||||
// Get retrieves the value and expiration of an individual secret
|
||||
func (s *SecretsManager) Get(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
value, expiration, found, err := s.getSecretById(secretPath)
|
||||
if err != nil {
|
||||
s.log.Error("unable to retrieve aws secret", err, lager.Data{
|
||||
"secretPath": secretPath,
|
||||
})
|
||||
return nil, nil, false, err
|
||||
}
|
||||
if found {
|
||||
return value, expiration, true, nil
|
||||
}
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -74,31 +65,26 @@ func (s *SecretsManager) Get(varDef varTemplate.VariableDefinition) (interface{}
|
|||
|
||||
In case SecretBinary is set, it is expected to be a valid JSON object or it will error.
|
||||
*/
|
||||
func (s *SecretsManager) getSecretById(name string) (interface{}, bool, error) {
|
||||
func (s *SecretsManager) getSecretById(name string) (interface{}, *time.Time, bool, error) {
|
||||
value, err := s.api.GetSecretValue(&secretsmanager.GetSecretValueInput{
|
||||
SecretId: &name,
|
||||
})
|
||||
if err == nil {
|
||||
switch {
|
||||
case value.SecretString != nil:
|
||||
return *value.SecretString, true, nil
|
||||
return *value.SecretString, nil, true, nil
|
||||
case value.SecretBinary != nil:
|
||||
values, err := decodeJsonValue(value.SecretBinary)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
return nil, nil, true, err
|
||||
}
|
||||
return values, true, nil
|
||||
return values, nil, true, nil
|
||||
}
|
||||
} else if errObj, ok := err.(awserr.Error); ok && errObj.Code() == secretsmanager.ErrCodeResourceNotFoundException {
|
||||
return nil, false, nil
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
func (s *SecretsManager) List() ([]varTemplate.VariableDefinition, error) {
|
||||
// not implemented, see vault implementation
|
||||
return []varTemplate.VariableDefinition{}, nil
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
func decodeJsonValue(data []byte) (map[interface{}]interface{}, error) {
|
||||
|
@ -112,3 +98,28 @@ func decodeJsonValue(data []byte) (map[interface{}]interface{}, error) {
|
|||
}
|
||||
return evenLessTyped, nil
|
||||
}
|
||||
|
||||
// SecretLookupPathAws is an implementation which returns an evaluated go text template
|
||||
type SecretLookupPathAws struct {
|
||||
NameTemplate *template.Template
|
||||
TeamName string
|
||||
PipelineName string
|
||||
}
|
||||
|
||||
func NewSecretLookupPathAws(nameTemplate *template.Template, teamName string, pipelineName string) creds.SecretLookupPath {
|
||||
return &SecretLookupPathAws{
|
||||
NameTemplate: nameTemplate,
|
||||
TeamName: teamName,
|
||||
PipelineName: pipelineName,
|
||||
}
|
||||
}
|
||||
|
||||
func (sl SecretLookupPathAws) VariableToSecretPath(varName string) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
err := sl.NameTemplate.Execute(&buf, &Secret{
|
||||
Team: sl.TeamName,
|
||||
Pipeline: sl.PipelineName,
|
||||
Secret: varName,
|
||||
})
|
||||
return buf.String(), err
|
||||
}
|
||||
|
|
|
@ -23,6 +23,6 @@ func NewSecretsManagerFactory(log lager.Logger, session *session.Session, secret
|
|||
}
|
||||
}
|
||||
|
||||
func (factory *secretsManagerFactory) NewVariables(teamName string, pipelineName string) creds.Variables {
|
||||
return NewSecretsManager(factory.log, factory.api, teamName, pipelineName, factory.secretTemplates)
|
||||
func (factory *secretsManagerFactory) NewSecrets() creds.Secrets {
|
||||
return NewSecretsManager(factory.log, factory.api, factory.secretTemplates)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package secretsmanager_test
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"text/template"
|
||||
|
||||
"code.cloudfoundry.org/lager"
|
||||
|
@ -37,6 +38,7 @@ func (mock *MockSecretsManagerService) GetSecretValue(input *secretsmanager.GetS
|
|||
|
||||
var _ = Describe("SecretsManager", func() {
|
||||
var secretAccess *SecretsManager
|
||||
var variables creds.Variables
|
||||
var varDef varTemplate.VariableDefinition
|
||||
var mockService MockSecretsManagerService
|
||||
|
||||
|
@ -48,7 +50,8 @@ var _ = Describe("SecretsManager", func() {
|
|||
t2, err := template.New("test").Parse(DefaultTeamSecretTemplate)
|
||||
Expect(t2).NotTo(BeNil())
|
||||
Expect(err).To(BeNil())
|
||||
secretAccess = NewSecretsManager(lager.NewLogger("secretsmanager_test"), &mockService, "alpha", "bogus", []*template.Template{t1, t2})
|
||||
secretAccess = NewSecretsManager(lager.NewLogger("secretsmanager_test"), &mockService, []*template.Template{t1, t2})
|
||||
variables = creds.NewVariables(secretAccess, "alpha", "bogus")
|
||||
Expect(secretAccess).NotTo(BeNil())
|
||||
mockService.stubGetParameter = func(input string) (*secretsmanager.GetSecretValueOutput, error) {
|
||||
if input == "/concourse/alpha/bogus/cheery" {
|
||||
|
@ -60,7 +63,7 @@ var _ = Describe("SecretsManager", func() {
|
|||
|
||||
Describe("Get()", func() {
|
||||
It("should get parameter if exists", func() {
|
||||
value, found, err := secretAccess.Get(varDef)
|
||||
value, found, err := variables.Get(varDef)
|
||||
Expect(value).To(BeEquivalentTo("secret value"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
@ -72,7 +75,7 @@ var _ = Describe("SecretsManager", func() {
|
|||
SecretBinary: []byte(`{"name": "yours", "pass": "truely"}`),
|
||||
}, nil
|
||||
}
|
||||
value, found, err := secretAccess.Get(varTemplate.VariableDefinition{Name: "user"})
|
||||
value, found, err := variables.Get(varTemplate.VariableDefinition{Name: "user"})
|
||||
Expect(err).To(BeNil())
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(value).To(BeEquivalentTo(map[interface{}]interface{}{
|
||||
|
@ -88,7 +91,7 @@ var _ = Describe("SecretsManager", func() {
|
|||
}
|
||||
return &secretsmanager.GetSecretValueOutput{SecretString: aws.String("team decrypted value")}, nil
|
||||
}
|
||||
value, found, err := secretAccess.Get(varDef)
|
||||
value, found, err := variables.Get(varDef)
|
||||
Expect(value).To(BeEquivalentTo("team decrypted value"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
@ -96,19 +99,19 @@ var _ = Describe("SecretsManager", func() {
|
|||
|
||||
It("should return not found on error", func() {
|
||||
mockService.stubGetParameter = nil
|
||||
value, found, err := secretAccess.Get(varDef)
|
||||
value, found, err := variables.Get(varDef)
|
||||
Expect(value).To(BeNil())
|
||||
Expect(found).To(BeFalse())
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
|
||||
It("should allow empty pipeline name", func() {
|
||||
secretAccess.PipelineName = ""
|
||||
variables := creds.NewVariables(secretAccess, "alpha", "")
|
||||
mockService.stubGetParameter = func(input string) (*secretsmanager.GetSecretValueOutput, error) {
|
||||
Expect(input).To(Equal("/concourse/alpha/cheery"))
|
||||
return &secretsmanager.GetSecretValueOutput{SecretString: aws.String("team power")}, nil
|
||||
}
|
||||
value, found, err := secretAccess.Get(varDef)
|
||||
value, found, err := variables.Get(varDef)
|
||||
Expect(value).To(BeEquivalentTo("team power"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
|
|
@ -93,7 +93,7 @@ func (manager *SsmManager) Health() (*creds.HealthResponse, error) {
|
|||
Method: "GetParameter",
|
||||
}
|
||||
|
||||
_, _, err := manager.Ssm.getParameterByName("__concourse-health-check")
|
||||
_, _, _, err := manager.Ssm.getParameterByName("__concourse-health-check")
|
||||
if err != nil {
|
||||
if errObj, ok := err.(awserr.Error); ok && strings.Contains(errObj.Code(), "AccessDenied") {
|
||||
health.Response = map[string]string{
|
||||
|
@ -153,7 +153,7 @@ func (manager *SsmManager) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (manager *SsmManager) NewVariablesFactory(log lager.Logger) (creds.VariablesFactory, error) {
|
||||
func (manager *SsmManager) NewSecretsFactory(log lager.Logger) (creds.SecretsFactory, error) {
|
||||
|
||||
session, err := manager.getSession()
|
||||
if err != nil {
|
||||
|
|
|
@ -2,104 +2,90 @@ package ssm
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"code.cloudfoundry.org/lager"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/ssm"
|
||||
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
|
||||
varTemplate "github.com/cloudfoundry/bosh-cli/director/template"
|
||||
)
|
||||
|
||||
type Ssm struct {
|
||||
log lager.Logger
|
||||
api ssmiface.SSMAPI
|
||||
TeamName string
|
||||
PipelineName string
|
||||
SecretTemplates []*template.Template
|
||||
secretTemplates []*template.Template
|
||||
}
|
||||
|
||||
func NewSsm(log lager.Logger, api ssmiface.SSMAPI, teamName string, pipelineName string, secretTemplates []*template.Template) *Ssm {
|
||||
func NewSsm(log lager.Logger, api ssmiface.SSMAPI, secretTemplates []*template.Template) *Ssm {
|
||||
return &Ssm{
|
||||
log: log,
|
||||
api: api,
|
||||
TeamName: teamName,
|
||||
PipelineName: pipelineName,
|
||||
SecretTemplates: secretTemplates,
|
||||
secretTemplates: secretTemplates,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Ssm) transformSecret(nameTemplate *template.Template, secret string) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
err := nameTemplate.Execute(&buf, &SsmSecret{
|
||||
Team: s.TeamName,
|
||||
Pipeline: s.PipelineName,
|
||||
Secret: secret,
|
||||
})
|
||||
return buf.String(), err
|
||||
}
|
||||
// NewSecretLookupPaths defines how variables will be searched in the underlying secret manager
|
||||
func (s *Ssm) NewSecretLookupPaths(teamName string, pipelineName string) []creds.SecretLookupPath {
|
||||
lookupPaths := []creds.SecretLookupPath{}
|
||||
for _, tmpl := range s.secretTemplates {
|
||||
lPath := NewSecretLookupPathSsm(tmpl, teamName, pipelineName)
|
||||
|
||||
func (s *Ssm) Get(varDef varTemplate.VariableDefinition) (interface{}, bool, error) {
|
||||
for _, st := range s.SecretTemplates {
|
||||
// Try to get the parameter as string value
|
||||
parameter, err := s.transformSecret(st, varDef.Name)
|
||||
if err != nil {
|
||||
s.log.Error("failed-to-build-ssm-parameter-path-from-secret", err, lager.Data{
|
||||
"template": st.Name(),
|
||||
"secret": varDef.Name,
|
||||
})
|
||||
return nil, false, err
|
||||
}
|
||||
// If pipeline name is empty, double slashes may be present in the parameter name
|
||||
if strings.Contains(parameter, "//") {
|
||||
continue
|
||||
}
|
||||
value, found, err := s.getParameterByName(parameter)
|
||||
if err != nil {
|
||||
s.log.Error("failed-to-get-ssm-parameter-by-name", err, lager.Data{
|
||||
"template": st.Name(),
|
||||
"secret": varDef.Name,
|
||||
"parameter": parameter,
|
||||
})
|
||||
return nil, false, err
|
||||
}
|
||||
if found {
|
||||
return value, true, nil
|
||||
}
|
||||
// // Paramter may exist as a complex value so try again using paramter name as root path
|
||||
value, found, err = s.getParameterByPath(parameter)
|
||||
if err != nil {
|
||||
s.log.Error("failed-to-get-ssm-parameter-by-path", err, lager.Data{
|
||||
"template": st.Name(),
|
||||
"secret": varDef.Name,
|
||||
"parameter": parameter,
|
||||
})
|
||||
return nil, false, err
|
||||
}
|
||||
if found {
|
||||
return value, true, nil
|
||||
// if pipeline name is empty, double slashes may be present in the rendered template
|
||||
// let's avoid adding these templates
|
||||
samplePath, err := lPath.VariableToSecretPath("variable")
|
||||
if err == nil && !strings.Contains(samplePath, "//") {
|
||||
lookupPaths = append(lookupPaths, lPath)
|
||||
}
|
||||
}
|
||||
return nil, false, nil
|
||||
return lookupPaths
|
||||
}
|
||||
|
||||
func (s *Ssm) getParameterByName(name string) (interface{}, bool, error) {
|
||||
// Get retrieves the value and expiration of an individual secret
|
||||
func (s *Ssm) Get(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
// Try to get the parameter as string value, by name
|
||||
value, expiration, found, err := s.getParameterByName(secretPath)
|
||||
if err != nil {
|
||||
s.log.Error("unable to retrieve aws ssm secret by name", err, lager.Data{
|
||||
"secretPath": secretPath,
|
||||
})
|
||||
return nil, nil, false, err
|
||||
}
|
||||
if found {
|
||||
return value, expiration, true, nil
|
||||
}
|
||||
// Parameter may exist as a complex value so try again using parameter name as root path
|
||||
value, expiration, found, err = s.getParameterByPath(secretPath)
|
||||
if err != nil {
|
||||
s.log.Error("unable to retrieve aws ssm secret by path", err, lager.Data{
|
||||
"secretPath": secretPath,
|
||||
})
|
||||
return nil, nil, false, err
|
||||
}
|
||||
if found {
|
||||
return value, expiration, true, nil
|
||||
}
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
||||
func (s *Ssm) getParameterByName(name string) (interface{}, *time.Time, bool, error) {
|
||||
param, err := s.api.GetParameter(&ssm.GetParameterInput{
|
||||
Name: &name,
|
||||
WithDecryption: aws.Bool(true),
|
||||
})
|
||||
if err == nil {
|
||||
return *param.Parameter.Value, true, nil
|
||||
return *param.Parameter.Value, nil, true, nil
|
||||
|
||||
} else if errObj, ok := err.(awserr.Error); ok && errObj.Code() == ssm.ErrCodeParameterNotFound {
|
||||
return nil, false, nil
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
return nil, false, err
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
func (s *Ssm) getParameterByPath(path string) (interface{}, bool, error) {
|
||||
func (s *Ssm) getParameterByPath(path string) (interface{}, *time.Time, bool, error) {
|
||||
path = strings.TrimRight(path, "/")
|
||||
if path == "" {
|
||||
path = "/"
|
||||
|
@ -114,15 +100,35 @@ func (s *Ssm) getParameterByPath(path string) (interface{}, bool, error) {
|
|||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, nil, false, err
|
||||
}
|
||||
if len(value) == 0 {
|
||||
return nil, false, nil
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
return value, true, nil
|
||||
return value, nil, true, nil
|
||||
}
|
||||
|
||||
func (s *Ssm) List() ([]varTemplate.VariableDefinition, error) {
|
||||
// not implemented, see vault implementation
|
||||
return []varTemplate.VariableDefinition{}, nil
|
||||
// SecretLookupPathSsm is an implementation which returns an evaluated go text template
|
||||
type SecretLookupPathSsm struct {
|
||||
NameTemplate *template.Template
|
||||
TeamName string
|
||||
PipelineName string
|
||||
}
|
||||
|
||||
func NewSecretLookupPathSsm(nameTemplate *template.Template, teamName string, pipelineName string) creds.SecretLookupPath {
|
||||
return &SecretLookupPathSsm{
|
||||
NameTemplate: nameTemplate,
|
||||
TeamName: teamName,
|
||||
PipelineName: pipelineName,
|
||||
}
|
||||
}
|
||||
|
||||
func (sl SecretLookupPathSsm) VariableToSecretPath(varName string) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
err := sl.NameTemplate.Execute(&buf, &SsmSecret{
|
||||
Team: sl.TeamName,
|
||||
Pipeline: sl.PipelineName,
|
||||
Secret: varName,
|
||||
})
|
||||
return buf.String(), err
|
||||
}
|
||||
|
|
|
@ -23,6 +23,6 @@ func NewSsmFactory(log lager.Logger, session *session.Session, secretTemplates [
|
|||
}
|
||||
}
|
||||
|
||||
func (factory *ssmFactory) NewVariables(teamName string, pipelineName string) creds.Variables {
|
||||
return NewSsm(factory.log, factory.api, teamName, pipelineName, factory.secretTemplates)
|
||||
func (factory *ssmFactory) NewSecrets() creds.Secrets {
|
||||
return NewSsm(factory.log, factory.api, factory.secretTemplates)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ssm_test
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"strconv"
|
||||
"text/template"
|
||||
|
||||
|
@ -84,6 +85,7 @@ func (mock *MockSsmService) GetParametersByPathPages(input *ssm.GetParametersByP
|
|||
|
||||
var _ = Describe("Ssm", func() {
|
||||
var ssmAccess *Ssm
|
||||
var variables creds.Variables
|
||||
var varDef varTemplate.VariableDefinition
|
||||
var mockService MockSsmService
|
||||
|
||||
|
@ -95,7 +97,8 @@ var _ = Describe("Ssm", func() {
|
|||
t2, err := template.New("test").Parse(DefaultTeamSecretTemplate)
|
||||
Expect(t2).NotTo(BeNil())
|
||||
Expect(err).To(BeNil())
|
||||
ssmAccess = NewSsm(lager.NewLogger("ssm_test"), &mockService, "alpha", "bogus", []*template.Template{t1, t2})
|
||||
ssmAccess = NewSsm(lager.NewLogger("ssm_test"), &mockService, []*template.Template{t1, t2})
|
||||
variables = creds.NewVariables(ssmAccess, "alpha", "bogus")
|
||||
Expect(ssmAccess).NotTo(BeNil())
|
||||
mockService.stubGetParameter = func(input string) (string, error) {
|
||||
if input == "/concourse/alpha/bogus/cheery" {
|
||||
|
@ -110,7 +113,7 @@ var _ = Describe("Ssm", func() {
|
|||
|
||||
Describe("Get()", func() {
|
||||
It("should get parameter if exists", func() {
|
||||
value, found, err := ssmAccess.Get(varDef)
|
||||
value, found, err := variables.Get(varDef)
|
||||
Expect(value).To(BeEquivalentTo("ssm decrypted value"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
@ -128,7 +131,7 @@ var _ = Describe("Ssm", func() {
|
|||
},
|
||||
}
|
||||
}
|
||||
value, found, err := ssmAccess.Get(varTemplate.VariableDefinition{Name: "user"})
|
||||
value, found, err := variables.Get(varTemplate.VariableDefinition{Name: "user"})
|
||||
Expect(value).To(BeEquivalentTo(map[interface{}]interface{}{
|
||||
"name": "yours",
|
||||
"pass": "truely",
|
||||
|
@ -141,7 +144,7 @@ var _ = Describe("Ssm", func() {
|
|||
mockService.stubGetParameter = func(input string) (string, error) {
|
||||
return "101", nil
|
||||
}
|
||||
value, found, err := ssmAccess.Get(varDef)
|
||||
value, found, err := variables.Get(varDef)
|
||||
Expect(value).To(BeEquivalentTo("101"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
@ -154,7 +157,7 @@ var _ = Describe("Ssm", func() {
|
|||
}
|
||||
return "team decrypted value", nil
|
||||
}
|
||||
value, found, err := ssmAccess.Get(varDef)
|
||||
value, found, err := variables.Get(varDef)
|
||||
Expect(value).To(BeEquivalentTo("team decrypted value"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
@ -162,19 +165,19 @@ var _ = Describe("Ssm", func() {
|
|||
|
||||
It("should return not found on error", func() {
|
||||
mockService.stubGetParameter = nil
|
||||
value, found, err := ssmAccess.Get(varDef)
|
||||
value, found, err := variables.Get(varDef)
|
||||
Expect(value).To(BeNil())
|
||||
Expect(found).To(BeFalse())
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
|
||||
It("should allow empty pipeline name", func() {
|
||||
ssmAccess.PipelineName = ""
|
||||
variables := creds.NewVariables(ssmAccess, "alpha", "")
|
||||
mockService.stubGetParameter = func(input string) (string, error) {
|
||||
Expect(input).To(Equal("/concourse/alpha/cheery"))
|
||||
return "team power", nil
|
||||
}
|
||||
value, found, err := ssmAccess.Get(varDef)
|
||||
value, found, err := variables.Get(varDef)
|
||||
Expect(value).To(BeEquivalentTo("team power"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
package creds
|
||||
|
||||
import "github.com/cloudfoundry/bosh-cli/director/template"
|
||||
|
||||
//go:generate counterfeiter . VariablesFactory
|
||||
|
||||
type VariablesFactory interface {
|
||||
NewVariables(string, string) Variables
|
||||
}
|
||||
|
||||
//go:generate counterfeiter . Variables
|
||||
|
||||
type Variables interface {
|
||||
Get(template.VariableDefinition) (interface{}, bool, error)
|
||||
List() ([]template.VariableDefinition, error)
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
vaultapi "github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
type cachedSecret struct {
|
||||
deadline time.Time
|
||||
secret *vaultapi.Secret
|
||||
}
|
||||
|
||||
// A Cache caches secrets read from a SecretReader until the lease on
|
||||
// the secret expires. Once expired the credential is proactively
|
||||
// deleted from cache to maintain a smaller cache footprint.
|
||||
type Cache struct {
|
||||
sync.RWMutex
|
||||
cache map[string]*cachedSecret
|
||||
newItems chan time.Time
|
||||
sr SecretReader
|
||||
maxLease time.Duration
|
||||
}
|
||||
|
||||
// TODO: Should a cache have a max size to
|
||||
// prevent unbounded growth?
|
||||
|
||||
// NewCache using the underlying vault client.
|
||||
func NewCache(sr SecretReader, maxLease time.Duration) *Cache {
|
||||
c := &Cache{
|
||||
cache: make(map[string]*cachedSecret),
|
||||
newItems: make(chan time.Time, 100),
|
||||
sr: sr,
|
||||
maxLease: maxLease,
|
||||
}
|
||||
go c.reaperThread()
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Cache) reapCache() time.Time {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
var smallestNext time.Time
|
||||
for k, secret := range c.cache {
|
||||
if time.Now().After(secret.deadline) {
|
||||
delete(c.cache, k)
|
||||
continue
|
||||
}
|
||||
if secret.deadline.Before(smallestNext) {
|
||||
smallestNext = secret.deadline
|
||||
}
|
||||
}
|
||||
return smallestNext
|
||||
}
|
||||
|
||||
func (c *Cache) reaperThread() {
|
||||
sleep := time.NewTimer(1 * time.Second)
|
||||
defer sleep.Stop()
|
||||
var nextWakeup time.Time
|
||||
for {
|
||||
select {
|
||||
case <-sleep.C:
|
||||
nextWakeup = c.reapCache()
|
||||
if !nextWakeup.IsZero() {
|
||||
sleep.Reset(time.Since(nextWakeup))
|
||||
}
|
||||
case t := <-c.newItems:
|
||||
if t.Before(nextWakeup) {
|
||||
continue
|
||||
}
|
||||
nextWakeup = t
|
||||
sleep.Reset(time.Until(nextWakeup))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read a secret from the cache or the underlying client if not
|
||||
// present.
|
||||
func (c *Cache) Read(path string) (*vaultapi.Secret, error) {
|
||||
// If we have the secret in our cache just return it
|
||||
c.RLock() // don't use defer because we want to agressively release this lock
|
||||
cs, cached := c.cache[path]
|
||||
c.RUnlock()
|
||||
|
||||
if cached && time.Now().Before(cs.deadline) {
|
||||
return cs.secret, nil
|
||||
}
|
||||
|
||||
// Otherwise fetch the secret using the client. Clients are
|
||||
// thread safe for read use.
|
||||
secret, err := c.sr.Read(path)
|
||||
if err != nil || secret == nil {
|
||||
return secret, err
|
||||
}
|
||||
|
||||
// We will renew the item in half the lease duration to resolve an inherent race
|
||||
// in this setup: What if the secret becomes invalid _during_ the build. We don't
|
||||
// want to be issuing secrets that expire in (fex) 100ms.
|
||||
// This is a problem in any implementation, as lease duration could be 1s and the
|
||||
// build will _probably_ take longer than that.
|
||||
dur := time.Duration(secret.LeaseDuration) * time.Second / 2
|
||||
if c.maxLease != 0 && dur > c.maxLease {
|
||||
dur = c.maxLease
|
||||
}
|
||||
|
||||
// Store the secret in cache
|
||||
cs = &cachedSecret{
|
||||
deadline: time.Now().Add(dur),
|
||||
secret: secret,
|
||||
}
|
||||
c.Lock()
|
||||
c.cache[path] = cs
|
||||
c.Unlock()
|
||||
|
||||
// Tell the reaper thread it has new items to cleanup
|
||||
c.newItems <- cs.deadline
|
||||
|
||||
return secret, nil
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
vaultapi "github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
type MockSecretReader struct {
|
||||
secrets []*vaultapi.Secret
|
||||
reads []string
|
||||
}
|
||||
|
||||
func (msr *MockSecretReader) Read(path string) (*vaultapi.Secret, error) {
|
||||
msr.reads = append(msr.reads, path)
|
||||
s := msr.secrets[0]
|
||||
msr.secrets = msr.secrets[1:]
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
secrets := []*vaultapi.Secret{
|
||||
&vaultapi.Secret{
|
||||
RequestID: "1",
|
||||
LeaseDuration: 1,
|
||||
},
|
||||
&vaultapi.Secret{
|
||||
RequestID: "1",
|
||||
LeaseDuration: 2,
|
||||
},
|
||||
&vaultapi.Secret{
|
||||
RequestID: "1",
|
||||
LeaseDuration: 10,
|
||||
},
|
||||
}
|
||||
msr := &MockSecretReader{
|
||||
secrets: secrets,
|
||||
}
|
||||
|
||||
cache := NewCache(msr, 5*time.Second)
|
||||
// miss
|
||||
secret, err := cache.Read("path1")
|
||||
if err != nil {
|
||||
t.Error("got error reading valid secret", err)
|
||||
}
|
||||
if secret.RequestID != secrets[0].RequestID {
|
||||
t.Errorf("read secret %s expected %s", secret.RequestID, secrets[0].RequestID)
|
||||
}
|
||||
if len(msr.reads) != 1 && msr.reads[0] != "path1" {
|
||||
t.Errorf("Got reads [%v], expected [\"%s\"]", msr.reads, "path1")
|
||||
}
|
||||
|
||||
// hit
|
||||
secret, err = cache.Read("path1")
|
||||
if err != nil {
|
||||
t.Error("got error reading valid secret from cache", err)
|
||||
}
|
||||
if secret.RequestID != secrets[0].RequestID {
|
||||
t.Errorf("read secret %s expected %s", secret.RequestID, secrets[0].RequestID)
|
||||
}
|
||||
if len(msr.reads) != 1 && msr.reads[0] != "path1" {
|
||||
t.Errorf("Got reads [%v], expected [\"%s\"]", msr.reads, "path1")
|
||||
}
|
||||
|
||||
// reap
|
||||
time.Sleep(time.Duration(secret.LeaseDuration)*time.Second + 100*time.Millisecond)
|
||||
|
||||
// miss
|
||||
secret, err = cache.Read("path1")
|
||||
if err != nil {
|
||||
t.Error("got error reading valid secret from cache", err)
|
||||
}
|
||||
if secret.RequestID != secrets[0].RequestID {
|
||||
t.Errorf("read secret %s expected %s", secret.RequestID, secrets[0].RequestID)
|
||||
}
|
||||
if len(msr.reads) != 2 && msr.reads[0] != "path1" {
|
||||
t.Errorf("Got reads [%v], expected [\"%s\"]", msr.reads, "path1 path1")
|
||||
}
|
||||
|
||||
// reap
|
||||
time.Sleep(time.Duration(secret.LeaseDuration)*time.Second + 100*time.Millisecond)
|
||||
cache.RLock()
|
||||
if len(cache.cache) != 0 {
|
||||
t.Errorf("Expectde cache to be clean after expiration, was %v", cache.cache)
|
||||
}
|
||||
cache.RUnlock()
|
||||
|
||||
// Test max duration
|
||||
secret, err = cache.Read("path1")
|
||||
if err != nil {
|
||||
t.Error("got error reading valid secret from cache", err)
|
||||
}
|
||||
if secret.RequestID != secrets[0].RequestID {
|
||||
t.Errorf("read secret %s expected %s", secret.RequestID, secrets[0].RequestID)
|
||||
}
|
||||
if len(msr.reads) != 1 && msr.reads[0] != "path1" {
|
||||
t.Errorf("Got reads [%v], expected [\"%s\"]", msr.reads, "path1")
|
||||
}
|
||||
|
||||
// reap
|
||||
time.Sleep(5*time.Second + 100*time.Millisecond)
|
||||
cache.RLock()
|
||||
if len(cache.cache) != 0 {
|
||||
t.Errorf("Expectde cache to be clean after maxu duration, was %v", cache.cache)
|
||||
}
|
||||
cache.RUnlock()
|
||||
|
||||
}
|
|
@ -19,9 +19,6 @@ type VaultManager struct {
|
|||
PathPrefix string `long:"path-prefix" default:"/concourse" description:"Path under which to namespace credential lookup."`
|
||||
SharedPath string `long:"shared-path" description:"Path under which to lookup shared credentials."`
|
||||
|
||||
Cache bool `long:"cache" description:"Cache returned secrets for their lease duration in memory"`
|
||||
MaxLease time.Duration `long:"max-lease" description:"If the cache is enabled, and this is set, override secrets lease duration with a maximum value"`
|
||||
|
||||
TLS TLS
|
||||
Auth AuthConfig
|
||||
Client *APIClient
|
||||
|
@ -77,8 +74,6 @@ func (manager *VaultManager) MarshalJSON() ([]byte, error) {
|
|||
return json.Marshal(&map[string]interface{}{
|
||||
"url": manager.URL,
|
||||
"path_prefix": manager.PathPrefix,
|
||||
"cache": manager.Cache,
|
||||
"max_lease": manager.MaxLease,
|
||||
"ca_cert": manager.TLS.CACert,
|
||||
"server_name": manager.TLS.ServerName,
|
||||
"auth_backend": manager.Auth.Backend,
|
||||
|
@ -125,12 +120,7 @@ func (manager VaultManager) Health() (*creds.HealthResponse, error) {
|
|||
return health, nil
|
||||
}
|
||||
|
||||
func (manager VaultManager) NewVariablesFactory(logger lager.Logger) (creds.VariablesFactory, error) {
|
||||
func (manager VaultManager) NewSecretsFactory(logger lager.Logger) (creds.SecretsFactory, error) {
|
||||
ra := NewReAuther(manager.Client, manager.Auth.BackendMaxTTL, manager.Auth.RetryInitial, manager.Auth.RetryMax)
|
||||
var sr SecretReader = manager.Client
|
||||
if manager.Cache {
|
||||
sr = NewCache(manager.Client, manager.MaxLease)
|
||||
}
|
||||
|
||||
return NewVaultFactory(sr, ra.LoggedIn(), manager.PathPrefix, manager.SharedPath), nil
|
||||
return NewVaultFactory(manager.Client, ra.LoggedIn(), manager.PathPrefix, manager.SharedPath), nil
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
vaultapi "github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
|
@ -17,46 +18,34 @@ type SecretReader interface {
|
|||
// data.
|
||||
type Vault struct {
|
||||
SecretReader SecretReader
|
||||
|
||||
PathPrefix string
|
||||
Prefix string
|
||||
SharedPath string
|
||||
TeamName string
|
||||
PipelineName string
|
||||
}
|
||||
|
||||
func (v Vault) Get(varDef template.VariableDefinition) (interface{}, bool, error) {
|
||||
var secret *vaultapi.Secret
|
||||
var found bool
|
||||
var err error
|
||||
|
||||
if v.PipelineName != "" {
|
||||
secret, found, err = v.findSecret(v.path(v.TeamName, v.PipelineName, varDef.Name))
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
// NewSecretLookupPaths defines how variables will be searched in the underlying secret manager
|
||||
func (v Vault) NewSecretLookupPaths(teamName string, pipelineName string) []creds.SecretLookupPath {
|
||||
lookupPaths := []creds.SecretLookupPath{}
|
||||
if len(pipelineName) > 0 {
|
||||
lookupPaths = append(lookupPaths, creds.NewSecretLookupWithPrefix(path.Join(v.Prefix, teamName, pipelineName)+"/"))
|
||||
}
|
||||
lookupPaths = append(lookupPaths, creds.NewSecretLookupWithPrefix(path.Join(v.Prefix, teamName)+"/"))
|
||||
lookupPaths = append(lookupPaths, creds.NewSecretLookupWithPrefix(path.Join(v.Prefix, v.SharedPath)+"/"))
|
||||
return lookupPaths
|
||||
}
|
||||
|
||||
// Get retrieves the value and expiration of an individual secret
|
||||
func (v Vault) Get(secretPath string) (interface{}, *time.Time, bool, error) {
|
||||
secret, expiration, found, err := v.findSecret(secretPath)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
if !found {
|
||||
secret, found, err = v.findSecret(v.path(v.TeamName, varDef.Name))
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
if !found && v.SharedPath != "" {
|
||||
secret, found, err = v.findSecret(v.path(v.SharedPath, varDef.Name))
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, false, nil
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
||||
val, found := secret.Data["value"]
|
||||
if found {
|
||||
return val, true, nil
|
||||
return val, expiration, true, nil
|
||||
}
|
||||
|
||||
evenLessTyped := map[interface{}]interface{}{}
|
||||
|
@ -64,41 +53,22 @@ func (v Vault) Get(varDef template.VariableDefinition) (interface{}, bool, error
|
|||
evenLessTyped[k] = v
|
||||
}
|
||||
|
||||
return evenLessTyped, true, nil
|
||||
return evenLessTyped, expiration, true, nil
|
||||
}
|
||||
|
||||
func (v Vault) findSecret(path string) (*vaultapi.Secret, bool, error) {
|
||||
func (v Vault) findSecret(path string) (*vaultapi.Secret, *time.Time, bool, error) {
|
||||
secret, err := v.SecretReader.Read(path)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
if secret != nil {
|
||||
return secret, true, nil
|
||||
// The lease duration is TTL: the time in seconds for which the lease is valid
|
||||
// A consumer of this secret must renew the lease within that time.
|
||||
duration := time.Duration(secret.LeaseDuration) * time.Second / 2
|
||||
expiration := time.Now().Add(duration)
|
||||
return secret, &expiration, true, nil
|
||||
}
|
||||
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (v Vault) path(segments ...string) string {
|
||||
return path.Join(append([]string{v.PathPrefix}, segments...)...)
|
||||
}
|
||||
|
||||
func (v Vault) List() ([]template.VariableDefinition, error) {
|
||||
// Don't think this works with vault.. if we need it to we'll figure it out
|
||||
// var defs []template.VariableDefinition
|
||||
|
||||
// secret, err := v.vaultClient.List(v.PathPrefix)
|
||||
// if err != nil {
|
||||
// return defs, err
|
||||
// }
|
||||
|
||||
// var def template.VariableDefinition
|
||||
// for name, _ := range secret.Data {
|
||||
// defs := append(defs, template.VariableDefinition{
|
||||
// Name: name,
|
||||
// })
|
||||
// }
|
||||
|
||||
return []template.VariableDefinition{}, nil
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
|
|
@ -8,26 +8,25 @@ import (
|
|||
|
||||
// The vaultFactory will return a vault implementation of creds.Variables.
|
||||
type vaultFactory struct {
|
||||
sr SecretReader
|
||||
prefix string
|
||||
sr SecretReader
|
||||
prefix string
|
||||
sharedPath string
|
||||
loggedIn <-chan struct{}
|
||||
loggedIn <-chan struct{}
|
||||
}
|
||||
|
||||
func NewVaultFactory(sr SecretReader, loggedIn <-chan struct{}, prefix string, sharedPath string) *vaultFactory {
|
||||
factory := &vaultFactory{
|
||||
sr: sr,
|
||||
prefix: prefix,
|
||||
sr: sr,
|
||||
prefix: prefix,
|
||||
sharedPath: sharedPath,
|
||||
loggedIn: loggedIn,
|
||||
loggedIn: loggedIn,
|
||||
}
|
||||
|
||||
return factory
|
||||
}
|
||||
|
||||
// NewVariables will block until the loggedIn channel passed to the
|
||||
// constructor signals a successful login.
|
||||
func (factory *vaultFactory) NewVariables(teamName string, pipelineName string) creds.Variables {
|
||||
// NewSecrets will block until the loggedIn channel passed to the constructor signals a successful login.
|
||||
func (factory *vaultFactory) NewSecrets() creds.Secrets {
|
||||
select {
|
||||
case <-factory.loggedIn:
|
||||
case <-time.After(5 * time.Second):
|
||||
|
@ -35,9 +34,7 @@ func (factory *vaultFactory) NewVariables(teamName string, pipelineName string)
|
|||
|
||||
return &Vault{
|
||||
SecretReader: factory.sr,
|
||||
PathPrefix: factory.prefix,
|
||||
Prefix: factory.prefix,
|
||||
SharedPath: factory.sharedPath,
|
||||
TeamName: teamName,
|
||||
PipelineName: pipelineName,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package vault_test
|
|||
|
||||
import (
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"github.com/concourse/concourse/atc/creds/vault"
|
||||
vaultapi "github.com/hashicorp/vault/api"
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
@ -21,7 +22,7 @@ func (msr *MockSecretReader) Read(lookupPath string) (*vaultapi.Secret, error) {
|
|||
Expect(lookupPath).ToNot(BeNil())
|
||||
|
||||
for _, secret := range *msr.secrets {
|
||||
if (lookupPath == secret.path) {
|
||||
if lookupPath == secret.path {
|
||||
return secret.secret, nil
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +33,7 @@ func (msr *MockSecretReader) Read(lookupPath string) (*vaultapi.Secret, error) {
|
|||
var _ = Describe("Vault", func() {
|
||||
|
||||
var v *vault.Vault
|
||||
var variables creds.Variables
|
||||
var msr *MockSecretReader
|
||||
|
||||
JustBeforeEach(func() {
|
||||
|
@ -47,12 +49,11 @@ var _ = Describe("Vault", func() {
|
|||
|
||||
v = &vault.Vault{
|
||||
SecretReader: msr,
|
||||
PathPrefix: "/concourse",
|
||||
Prefix: "/concourse",
|
||||
SharedPath: "shared",
|
||||
TeamName: "team",
|
||||
PipelineName: "pipeline",
|
||||
}
|
||||
|
||||
variables = creds.NewVariables(v, "team", "pipeline")
|
||||
})
|
||||
|
||||
Describe("Get()", func() {
|
||||
|
@ -65,7 +66,7 @@ var _ = Describe("Vault", func() {
|
|||
},
|
||||
}},
|
||||
}
|
||||
value, found, err := v.Get(template.VariableDefinition{Name: "foo"})
|
||||
value, found, err := variables.Get(template.VariableDefinition{Name: "foo"})
|
||||
Expect(value).To(BeEquivalentTo("bar"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
@ -80,7 +81,7 @@ var _ = Describe("Vault", func() {
|
|||
},
|
||||
}},
|
||||
}
|
||||
value, found, err := v.Get(template.VariableDefinition{Name: "foo"})
|
||||
value, found, err := variables.Get(template.VariableDefinition{Name: "foo"})
|
||||
Expect(value).To(BeEquivalentTo("bar"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
@ -95,7 +96,7 @@ var _ = Describe("Vault", func() {
|
|||
},
|
||||
}},
|
||||
}
|
||||
value, found, err := v.Get(template.VariableDefinition{Name: "foo"})
|
||||
value, found, err := variables.Get(template.VariableDefinition{Name: "foo"})
|
||||
Expect(value).To(BeEquivalentTo("bar"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
@ -116,7 +117,7 @@ var _ = Describe("Vault", func() {
|
|||
},
|
||||
}},
|
||||
}
|
||||
value, found, err := v.Get(template.VariableDefinition{Name: "foo"})
|
||||
value, found, err := variables.Get(template.VariableDefinition{Name: "foo"})
|
||||
Expect(value).To(BeEquivalentTo("bar"))
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(err).To(BeNil())
|
||||
|
|
|
@ -110,13 +110,13 @@ type FakeTeam struct {
|
|||
deleteReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
FindCheckContainersStub func(lager.Logger, string, string, creds.VariablesFactory) ([]db.Container, map[int]time.Time, error)
|
||||
FindCheckContainersStub func(lager.Logger, string, string, creds.Secrets) ([]db.Container, map[int]time.Time, error)
|
||||
findCheckContainersMutex sync.RWMutex
|
||||
findCheckContainersArgsForCall []struct {
|
||||
arg1 lager.Logger
|
||||
arg2 string
|
||||
arg3 string
|
||||
arg4 creds.VariablesFactory
|
||||
arg4 creds.Secrets
|
||||
}
|
||||
findCheckContainersReturns struct {
|
||||
result1 []db.Container
|
||||
|
@ -879,14 +879,14 @@ func (fake *FakeTeam) DeleteReturnsOnCall(i int, result1 error) {
|
|||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeTeam) FindCheckContainers(arg1 lager.Logger, arg2 string, arg3 string, arg4 creds.VariablesFactory) ([]db.Container, map[int]time.Time, error) {
|
||||
func (fake *FakeTeam) FindCheckContainers(arg1 lager.Logger, arg2 string, arg3 string, arg4 creds.Secrets) ([]db.Container, map[int]time.Time, error) {
|
||||
fake.findCheckContainersMutex.Lock()
|
||||
ret, specificReturn := fake.findCheckContainersReturnsOnCall[len(fake.findCheckContainersArgsForCall)]
|
||||
fake.findCheckContainersArgsForCall = append(fake.findCheckContainersArgsForCall, struct {
|
||||
arg1 lager.Logger
|
||||
arg2 string
|
||||
arg3 string
|
||||
arg4 creds.VariablesFactory
|
||||
arg4 creds.Secrets
|
||||
}{arg1, arg2, arg3, arg4})
|
||||
fake.recordInvocation("FindCheckContainers", []interface{}{arg1, arg2, arg3, arg4})
|
||||
fake.findCheckContainersMutex.Unlock()
|
||||
|
@ -906,13 +906,13 @@ func (fake *FakeTeam) FindCheckContainersCallCount() int {
|
|||
return len(fake.findCheckContainersArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeTeam) FindCheckContainersCalls(stub func(lager.Logger, string, string, creds.VariablesFactory) ([]db.Container, map[int]time.Time, error)) {
|
||||
func (fake *FakeTeam) FindCheckContainersCalls(stub func(lager.Logger, string, string, creds.Secrets) ([]db.Container, map[int]time.Time, error)) {
|
||||
fake.findCheckContainersMutex.Lock()
|
||||
defer fake.findCheckContainersMutex.Unlock()
|
||||
fake.FindCheckContainersStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeTeam) FindCheckContainersArgsForCall(i int) (lager.Logger, string, string, creds.VariablesFactory) {
|
||||
func (fake *FakeTeam) FindCheckContainersArgsForCall(i int) (lager.Logger, string, string, creds.Secrets) {
|
||||
fake.findCheckContainersMutex.RLock()
|
||||
defer fake.findCheckContainersMutex.RUnlock()
|
||||
argsForCall := fake.findCheckContainersArgsForCall[i]
|
||||
|
|
|
@ -59,7 +59,7 @@ type Team interface {
|
|||
IsContainerWithinTeam(string, bool) (bool, error)
|
||||
|
||||
FindContainerByHandle(string) (Container, bool, error)
|
||||
FindCheckContainers(lager.Logger, string, string, creds.VariablesFactory) ([]Container, map[int]time.Time, error)
|
||||
FindCheckContainers(lager.Logger, string, string, creds.Secrets) ([]Container, map[int]time.Time, error)
|
||||
FindContainersByMetadata(ContainerMetadata) ([]Container, error)
|
||||
FindCreatedContainerByHandle(string) (CreatedContainer, bool, error)
|
||||
FindWorkerForContainer(handle string) (Worker, bool, error)
|
||||
|
@ -803,7 +803,7 @@ func (t *team) UpdateProviderAuth(auth atc.TeamAuth) error {
|
|||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (t *team) FindCheckContainers(logger lager.Logger, pipelineName string, resourceName string, variablesFactory creds.VariablesFactory) ([]Container, map[int]time.Time, error) {
|
||||
func (t *team) FindCheckContainers(logger lager.Logger, pipelineName string, resourceName string, secretManager creds.Secrets) ([]Container, map[int]time.Time, error) {
|
||||
pipeline, found, err := t.Pipeline(pipelineName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -825,7 +825,7 @@ func (t *team) FindCheckContainers(logger lager.Logger, pipelineName string, res
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
variables := variablesFactory.NewVariables(t.name, pipeline.Name())
|
||||
variables := creds.NewVariables(secretManager, t.name, pipeline.Name())
|
||||
|
||||
versionedResourceTypes := pipelineResourceTypes.Deserialize()
|
||||
|
||||
|
|
|
@ -408,8 +408,7 @@ var _ = Describe("Team", func() {
|
|||
|
||||
Describe("Containers", func() {
|
||||
var (
|
||||
fakeVariablesFactory *credsfakes.FakeVariablesFactory
|
||||
variables creds.Variables
|
||||
fakeSecretManager *credsfakes.FakeSecrets
|
||||
resourceContainer db.CreatingContainer
|
||||
resourceConfigScope db.ResourceConfigScope
|
||||
firstContainerCreating db.CreatingContainer
|
||||
|
@ -417,9 +416,8 @@ var _ = Describe("Team", func() {
|
|||
|
||||
Context("when there is a task container and a check container", func() {
|
||||
BeforeEach(func() {
|
||||
fakeVariablesFactory = new(credsfakes.FakeVariablesFactory)
|
||||
variables = template.StaticVariables{}
|
||||
fakeVariablesFactory.NewVariablesReturns(variables)
|
||||
fakeSecretManager = new(credsfakes.FakeSecrets)
|
||||
fakeSecretManager.GetReturns("", nil, false, nil)
|
||||
|
||||
job, found, err := defaultPipeline.Job("some-job")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
@ -440,7 +438,7 @@ var _ = Describe("Team", func() {
|
|||
pipelineResourceTypes, err := defaultPipeline.ResourceTypes()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
resourceConfigScope, err = defaultResource.SetResourceConfig(logger, defaultResource.Source(), creds.NewVersionedResourceTypes(variables, pipelineResourceTypes.Deserialize()))
|
||||
resourceConfigScope, err = defaultResource.SetResourceConfig(logger, defaultResource.Source(), creds.NewVersionedResourceTypes(template.StaticVariables{}, pipelineResourceTypes.Deserialize()))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
resourceContainer, err = defaultWorker.CreateContainer(
|
||||
|
@ -2436,8 +2434,8 @@ var _ = Describe("Team", func() {
|
|||
|
||||
Describe("FindCheckContainers", func() {
|
||||
var (
|
||||
fakeVariablesFactory *credsfakes.FakeVariablesFactory
|
||||
variables creds.Variables
|
||||
fakeSecretManager *credsfakes.FakeSecrets
|
||||
variables creds.Variables
|
||||
)
|
||||
|
||||
expiries := db.ContainerOwnerExpiries{
|
||||
|
@ -2447,9 +2445,8 @@ var _ = Describe("Team", func() {
|
|||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
fakeVariablesFactory = new(credsfakes.FakeVariablesFactory)
|
||||
variables = template.StaticVariables{}
|
||||
fakeVariablesFactory.NewVariablesReturns(variables)
|
||||
fakeSecretManager = new(credsfakes.FakeSecrets)
|
||||
fakeSecretManager.GetReturns("", nil, false, nil)
|
||||
})
|
||||
|
||||
Context("when pipeline exists", func() {
|
||||
|
@ -2478,7 +2475,7 @@ var _ = Describe("Team", func() {
|
|||
})
|
||||
|
||||
It("returns check container for resource", func() {
|
||||
containers, checkContainersExpiresAt, err := defaultTeam.FindCheckContainers(logger, "default-pipeline", "some-resource", fakeVariablesFactory)
|
||||
containers, checkContainersExpiresAt, err := defaultTeam.FindCheckContainers(logger, "default-pipeline", "some-resource", fakeSecretManager)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(containers).To(HaveLen(1))
|
||||
Expect(containers[0].ID()).To(Equal(resourceContainer.ID()))
|
||||
|
@ -2528,7 +2525,7 @@ var _ = Describe("Team", func() {
|
|||
})
|
||||
|
||||
It("returns the same check container", func() {
|
||||
containers, checkContainersExpiresAt, err := defaultTeam.FindCheckContainers(logger, "other-pipeline", "some-resource", fakeVariablesFactory)
|
||||
containers, checkContainersExpiresAt, err := defaultTeam.FindCheckContainers(logger, "other-pipeline", "some-resource", fakeSecretManager)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(containers).To(HaveLen(1))
|
||||
Expect(containers[0].ID()).To(Equal(otherResourceContainer.ID()))
|
||||
|
@ -2541,7 +2538,7 @@ var _ = Describe("Team", func() {
|
|||
|
||||
Context("when check container does not exist", func() {
|
||||
It("returns empty list", func() {
|
||||
containers, checkContainersExpiresAt, err := defaultTeam.FindCheckContainers(logger, "default-pipeline", "some-resource", fakeVariablesFactory)
|
||||
containers, checkContainersExpiresAt, err := defaultTeam.FindCheckContainers(logger, "default-pipeline", "some-resource", fakeSecretManager)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(containers).To(BeEmpty())
|
||||
Expect(checkContainersExpiresAt).To(BeEmpty())
|
||||
|
@ -2551,7 +2548,7 @@ var _ = Describe("Team", func() {
|
|||
|
||||
Context("when resource does not exist", func() {
|
||||
It("returns empty list", func() {
|
||||
containers, checkContainersExpiresAt, err := defaultTeam.FindCheckContainers(logger, "default-pipeline", "non-existent-resource", fakeVariablesFactory)
|
||||
containers, checkContainersExpiresAt, err := defaultTeam.FindCheckContainers(logger, "default-pipeline", "non-existent-resource", fakeSecretManager)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(containers).To(BeEmpty())
|
||||
Expect(checkContainersExpiresAt).To(BeEmpty())
|
||||
|
@ -2561,7 +2558,7 @@ var _ = Describe("Team", func() {
|
|||
|
||||
Context("when pipeline does not exist", func() {
|
||||
It("returns empty list", func() {
|
||||
containers, checkContainersExpiresAt, err := defaultTeam.FindCheckContainers(logger, "non-existent-pipeline", "some-resource", fakeVariablesFactory)
|
||||
containers, checkContainersExpiresAt, err := defaultTeam.FindCheckContainers(logger, "non-existent-pipeline", "some-resource", fakeSecretManager)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(containers).To(BeEmpty())
|
||||
Expect(checkContainersExpiresAt).To(BeEmpty())
|
||||
|
|
|
@ -20,7 +20,7 @@ type stepFactory struct {
|
|||
resourceFetcher resource.Fetcher
|
||||
resourceCacheFactory db.ResourceCacheFactory
|
||||
resourceConfigFactory db.ResourceConfigFactory
|
||||
variablesFactory creds.VariablesFactory
|
||||
secretManager creds.Secrets
|
||||
defaultLimits atc.ContainerLimits
|
||||
strategy worker.ContainerPlacementStrategy
|
||||
resourceFactory resource.ResourceFactory
|
||||
|
@ -32,7 +32,7 @@ func NewStepFactory(
|
|||
resourceFetcher resource.Fetcher,
|
||||
resourceCacheFactory db.ResourceCacheFactory,
|
||||
resourceConfigFactory db.ResourceConfigFactory,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
defaultLimits atc.ContainerLimits,
|
||||
strategy worker.ContainerPlacementStrategy,
|
||||
resourceFactory resource.ResourceFactory,
|
||||
|
@ -43,7 +43,7 @@ func NewStepFactory(
|
|||
resourceFetcher: resourceFetcher,
|
||||
resourceCacheFactory: resourceCacheFactory,
|
||||
resourceConfigFactory: resourceConfigFactory,
|
||||
variablesFactory: variablesFactory,
|
||||
secretManager: secretManager,
|
||||
defaultLimits: defaultLimits,
|
||||
strategy: strategy,
|
||||
resourceFactory: resourceFactory,
|
||||
|
@ -59,7 +59,7 @@ func (factory *stepFactory) GetStep(
|
|||
) exec.Step {
|
||||
workerMetadata.WorkingDirectory = resource.ResourcesDir("get")
|
||||
|
||||
variables := factory.variablesFactory.NewVariables(build.TeamName(), build.PipelineName())
|
||||
variables := creds.NewVariables(factory.secretManager, build.TeamName(), build.PipelineName())
|
||||
|
||||
getStep := exec.NewGetStep(
|
||||
build,
|
||||
|
@ -99,7 +99,7 @@ func (factory *stepFactory) PutStep(
|
|||
) exec.Step {
|
||||
workerMetadata.WorkingDirectory = resource.ResourcesDir("put")
|
||||
|
||||
variables := factory.variablesFactory.NewVariables(build.TeamName(), build.PipelineName())
|
||||
variables := creds.NewVariables(factory.secretManager, build.TeamName(), build.PipelineName())
|
||||
|
||||
var putInputs exec.PutInputs
|
||||
if plan.Put.Inputs == nil {
|
||||
|
@ -152,7 +152,7 @@ func (factory *stepFactory) TaskStep(
|
|||
|
||||
containerMetadata.WorkingDirectory = workingDirectory
|
||||
|
||||
credMgrVariables := factory.variablesFactory.NewVariables(build.TeamName(), build.PipelineName())
|
||||
credMgrVariables := creds.NewVariables(factory.secretManager, build.TeamName(), build.PipelineName())
|
||||
|
||||
var taskConfigSource exec.TaskConfigSource
|
||||
var taskVars []template.Variables
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"io/ioutil"
|
||||
|
||||
"code.cloudfoundry.org/lager/lagertest"
|
||||
"github.com/cloudfoundry/bosh-cli/director/template"
|
||||
"github.com/concourse/concourse/atc"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"github.com/concourse/concourse/atc/creds/credsfakes"
|
||||
|
@ -40,7 +39,7 @@ var _ = Describe("GetStep", func() {
|
|||
fakeStrategy *workerfakes.FakeContainerPlacementStrategy
|
||||
fakeResourceFetcher *resourcefakes.FakeFetcher
|
||||
fakeResourceCacheFactory *dbfakes.FakeResourceCacheFactory
|
||||
fakeVariablesFactory *credsfakes.FakeVariablesFactory
|
||||
fakeSecretManager *credsfakes.FakeSecrets
|
||||
variables creds.Variables
|
||||
fakeBuild *dbfakes.FakeBuild
|
||||
fakeDelegate *execfakes.FakeGetDelegate
|
||||
|
@ -78,11 +77,9 @@ var _ = Describe("GetStep", func() {
|
|||
fakeStrategy = new(workerfakes.FakeContainerPlacementStrategy)
|
||||
fakeResourceCacheFactory = new(dbfakes.FakeResourceCacheFactory)
|
||||
|
||||
fakeVariablesFactory = new(credsfakes.FakeVariablesFactory)
|
||||
variables = template.StaticVariables{
|
||||
"source-param": "super-secret-source",
|
||||
}
|
||||
fakeVariablesFactory.NewVariablesReturns(variables)
|
||||
fakeSecretManager = new(credsfakes.FakeSecrets)
|
||||
fakeSecretManager.GetReturns("super-secret-source", nil, true, nil)
|
||||
variables = creds.NewVariables(fakeSecretManager, "team", "pipeline")
|
||||
|
||||
artifactRepository = artifact.NewRepository()
|
||||
state = new(execfakes.FakeRunState)
|
||||
|
@ -132,7 +129,7 @@ var _ = Describe("GetStep", func() {
|
|||
Get: getPlan,
|
||||
}
|
||||
|
||||
variables := fakeVariablesFactory.NewVariables(fakeBuild.TeamName(), fakeBuild.PipelineName())
|
||||
variables := creds.NewVariables(fakeSecretManager, fakeBuild.TeamName(), fakeBuild.PipelineName())
|
||||
|
||||
getStep = exec.NewGetStep(
|
||||
fakeBuild,
|
||||
|
|
|
@ -26,7 +26,7 @@ type scannerFactory struct {
|
|||
resourceTypeCheckingInterval time.Duration
|
||||
resourceCheckingInterval time.Duration
|
||||
externalURL string
|
||||
variablesFactory creds.VariablesFactory
|
||||
secretManager creds.Secrets
|
||||
strategy worker.ContainerPlacementStrategy
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ func NewScannerFactory(
|
|||
resourceTypeCheckingInterval time.Duration,
|
||||
resourceCheckingInterval time.Duration,
|
||||
externalURL string,
|
||||
variablesFactory creds.VariablesFactory,
|
||||
secretManager creds.Secrets,
|
||||
strategy worker.ContainerPlacementStrategy,
|
||||
) ScannerFactory {
|
||||
return &scannerFactory{
|
||||
|
@ -53,13 +53,13 @@ func NewScannerFactory(
|
|||
resourceCheckingInterval: resourceCheckingInterval,
|
||||
resourceTypeCheckingInterval: resourceTypeCheckingInterval,
|
||||
externalURL: externalURL,
|
||||
variablesFactory: variablesFactory,
|
||||
secretManager: secretManager,
|
||||
strategy: strategy,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *scannerFactory) NewResourceScanner(dbPipeline db.Pipeline) Scanner {
|
||||
variables := f.variablesFactory.NewVariables(dbPipeline.TeamName(), dbPipeline.Name())
|
||||
variables := creds.NewVariables(f.secretManager, dbPipeline.TeamName(), dbPipeline.Name())
|
||||
|
||||
return NewResourceScanner(
|
||||
clock.NewClock(),
|
||||
|
@ -75,7 +75,7 @@ func (f *scannerFactory) NewResourceScanner(dbPipeline db.Pipeline) Scanner {
|
|||
}
|
||||
|
||||
func (f *scannerFactory) NewResourceTypeScanner(dbPipeline db.Pipeline) Scanner {
|
||||
variables := f.variablesFactory.NewVariables(dbPipeline.TeamName(), dbPipeline.Name())
|
||||
variables := creds.NewVariables(f.secretManager, dbPipeline.TeamName(), dbPipeline.Name())
|
||||
|
||||
return NewResourceTypeScanner(
|
||||
clock.NewClock(),
|
||||
|
|
2
go.mod
2
go.mod
|
@ -168,7 +168,7 @@ require (
|
|||
github.com/ory-am/common v0.4.0 // indirect
|
||||
github.com/ory/dockertest v3.3.2+incompatible // indirect
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pborman/uuid v1.2.0 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/peterhellberg/link v1.0.0
|
||||
|
|
1
go.sum
1
go.sum
|
@ -135,6 +135,7 @@ github.com/coreos/etcd v3.2.9+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc3
|
|||
github.com/coreos/etcd v3.3.12+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-oidc v0.0.0-20170307191026-be73733bb8cc h1:9yuvA19Q5WFkLwJcMDoYm8m89ilzqZ5zEHqdvU+Zbds=
|
||||
github.com/coreos/go-oidc v0.0.0-20170307191026-be73733bb8cc/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-oidc v2.0.0+incompatible h1:+RStIopZ8wooMx+Vs5Bt8zMXxV1ABl5LbakNExNmZIg=
|
||||
github.com/coreos/go-oidc v2.0.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
|
|
Loading…
Reference in New Issue