helpers for sourcing global environment variables
parent
005c105d56
commit
77885dcc75
@ -0,0 +1,36 @@
|
||||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/drone/runner-go/environ"
|
||||
)
|
||||
|
||||
// Combine returns a new combined environment provider,
|
||||
// capable of sourcing environment variables from multiple
|
||||
// providers.
|
||||
func Combine(sources ...Provider) Provider {
|
||||
return &combined{sources}
|
||||
}
|
||||
|
||||
type combined struct {
|
||||
sources []Provider
|
||||
}
|
||||
|
||||
func (p *combined) List(ctx context.Context, in *Request) (map[string]string, error) {
|
||||
out := map[string]string{}
|
||||
for _, source := range p.sources {
|
||||
got, err := source.List(ctx, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if got != nil {
|
||||
out = environ.Combine(got, out)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCombine(t *testing.T) {
|
||||
a := map[string]string{"a": "b"}
|
||||
b := map[string]string{"c": "d"}
|
||||
aa := Static(a)
|
||||
bb := Static(b)
|
||||
p := Combine(aa, bb)
|
||||
out, err := p.List(noContext, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if len(out) != 2 {
|
||||
t.Errorf("Expect combined variable output")
|
||||
return
|
||||
}
|
||||
if out["a"] != "b" {
|
||||
t.Errorf("Missing variable")
|
||||
}
|
||||
if out["c"] != "d" {
|
||||
t.Errorf("Missing variable")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCombineError(t *testing.T) {
|
||||
e := errors.New("not found")
|
||||
m := mockProvider{err: e}
|
||||
p := Combine(&m)
|
||||
_, err := p.List(noContext, nil)
|
||||
if err != e {
|
||||
t.Errorf("Expect error")
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone-go/plugin/environ"
|
||||
"github.com/drone/runner-go/logger"
|
||||
)
|
||||
|
||||
// External returns a new external environment variable
|
||||
// provider. This provider makes an external API call to
|
||||
// list and return environment variables.
|
||||
func External(endpoint, token string, insecure bool) Provider {
|
||||
provider := &external{}
|
||||
if endpoint != "" {
|
||||
provider.client = environ.Client(endpoint, token, insecure)
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
type external struct {
|
||||
client environ.Plugin
|
||||
}
|
||||
|
||||
func (p *external) List(ctx context.Context, in *Request) (map[string]string, error) {
|
||||
if p.client == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
logger := logger.FromContext(ctx)
|
||||
|
||||
// include a timeout to prevent an API call from
|
||||
// hanging the build process indefinitely. The
|
||||
// external service must return a request within
|
||||
// one minute.
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Minute)
|
||||
defer cancel()
|
||||
|
||||
req := &environ.Request{
|
||||
Repo: *in.Repo,
|
||||
Build: *in.Build,
|
||||
}
|
||||
res, err := p.client.List(ctx, req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Debug("environment: external: cannot get environment variable list")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if no error is returned and the list is empty,
|
||||
// this indicates the client returned No Content,
|
||||
// and we should exit with no credentials, but no error.
|
||||
if len(res) == 0 {
|
||||
logger.Trace("environment: external: environment variable list is empty")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
logger.Trace("environment: external: environment variable list returned")
|
||||
|
||||
return res, nil
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone-go/drone"
|
||||
"github.com/drone/drone-go/plugin/environ"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestExternal(t *testing.T) {
|
||||
req := &Request{
|
||||
Build: &drone.Build{Event: drone.EventPush},
|
||||
Repo: &drone.Repo{Private: false},
|
||||
}
|
||||
want := map[string]string{"a": "b"}
|
||||
provider := External("http://localhost", "secret", false)
|
||||
provider.(*external).client = &mockPlugin{out: want}
|
||||
got, err := provider.List(noContext, req)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(got, want); diff != "" {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that if the remote API call to the
|
||||
// external plugin returns an error, the provider returns the
|
||||
// error to the caller.
|
||||
func TestExternal_ClientError(t *testing.T) {
|
||||
req := &Request{
|
||||
Build: &drone.Build{Event: drone.EventPush},
|
||||
Repo: &drone.Repo{Private: false},
|
||||
}
|
||||
want := errors.New("Not Found")
|
||||
provider := External("http://localhost", "secret", false)
|
||||
provider.(*external).client = &mockPlugin{err: want}
|
||||
_, got := provider.List(noContext, req)
|
||||
if got != want {
|
||||
t.Errorf("Want error %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that if no endpoint is configured the
|
||||
// provider exits immediately and returns a nil slice and nil
|
||||
// error.
|
||||
func TestExternal_NoEndpoint(t *testing.T) {
|
||||
provider := External("", "", false)
|
||||
res, err := provider.List(noContext, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Expect nil error, provider disabled")
|
||||
}
|
||||
if res != nil {
|
||||
t.Errorf("Expect nil secret, provider disabled")
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that nil credentials and a nil error
|
||||
// are returned if the registry endpoint returns no content.
|
||||
func TestExternal_NotFound(t *testing.T) {
|
||||
req := &Request{
|
||||
Repo: &drone.Repo{},
|
||||
Build: &drone.Build{},
|
||||
}
|
||||
provider := External("http://localhost", "secret", false)
|
||||
provider.(*external).client = &mockPlugin{}
|
||||
res, err := provider.List(noContext, req)
|
||||
if err != nil {
|
||||
t.Errorf("Expect nil error, registry list empty")
|
||||
}
|
||||
if res != nil {
|
||||
t.Errorf("Expect nil registry credentials")
|
||||
}
|
||||
}
|
||||
|
||||
type mockPlugin struct {
|
||||
out map[string]string
|
||||
err error
|
||||
}
|
||||
|
||||
func (m *mockPlugin) List(context.Context, *environ.Request) (map[string]string, error) {
|
||||
return m.out, m.err
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package provider provides environment variables to
|
||||
// a pipeline.
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/drone/drone-go/drone"
|
||||
)
|
||||
|
||||
// Request provides arguments for requesting a environment
|
||||
// variables from an environment Provider.
|
||||
type Request struct {
|
||||
Repo *drone.Repo
|
||||
Build *drone.Build
|
||||
}
|
||||
|
||||
// Provider is the interface that must be implemented by an
|
||||
// environment provider.
|
||||
type Provider interface {
|
||||
// List returns a list of environment variables.
|
||||
List(context.Context, *Request) (map[string]string, error)
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
var noContext = context.Background()
|
||||
|
||||
type mockProvider struct {
|
||||
out map[string]string
|
||||
err error
|
||||
}
|
||||
|
||||
func (p *mockProvider) List(context.Context, *Request) (map[string]string, error) {
|
||||
return p.out, p.err
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package provider
|
||||
|
||||
import "context"
|
||||
|
||||
// Static returns a new static environment variable provider.
|
||||
// The static provider finds and returns the static list
|
||||
// of static environment variables.
|
||||
func Static(params map[string]string) Provider {
|
||||
return &static{params}
|
||||
}
|
||||
|
||||
type static struct {
|
||||
params map[string]string
|
||||
}
|
||||
|
||||
func (p *static) List(context.Context, *Request) (map[string]string, error) {
|
||||
return p.params, nil
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStatic(t *testing.T) {
|
||||
a := map[string]string{"a": "b"}
|
||||
p := Static(a)
|
||||
b, err := p.List(noContext, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(a, b) {
|
||||
t.Errorf("Unexpected environment variable output")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue