github.com/drone/runner-go@v1.12.0/secret/external.go (about)

     1  // Copyright 2019 Drone.IO Inc. All rights reserved.
     2  // Use of this source code is governed by the Polyform License
     3  // that can be found in the LICENSE file.
     4  
     5  package secret
     6  
     7  import (
     8  	"context"
     9  	"time"
    10  
    11  	"github.com/drone/runner-go/logger"
    12  	"github.com/drone/runner-go/manifest"
    13  
    14  	"github.com/drone/drone-go/drone"
    15  	"github.com/drone/drone-go/plugin/secret"
    16  )
    17  
    18  // External returns a new external secret provider. The
    19  // external secret provider makes an external API call to find
    20  // and return a named secret.
    21  func External(endpoint, token string, insecure bool) Provider {
    22  	provider := &external{}
    23  	if endpoint != "" {
    24  		provider.client = secret.Client(endpoint, token, insecure)
    25  	}
    26  	return provider
    27  }
    28  
    29  type external struct {
    30  	client secret.Plugin
    31  }
    32  
    33  func (p *external) Find(ctx context.Context, in *Request) (*drone.Secret, error) {
    34  	if p.client == nil {
    35  		return nil, nil
    36  	}
    37  
    38  	logger := logger.FromContext(ctx).
    39  		WithField("name", in.Name).
    40  		WithField("kind", "secret")
    41  
    42  	// lookup the named secret in the manifest. If the
    43  	// secret does not exist, return a nil variable,
    44  	// allowing the next secret controller in the chain
    45  	// to be invoked.
    46  	path, name, ok := getExternal(in.Conf, in.Name)
    47  	if !ok {
    48  		logger.Trace("secret: external: no matching secret")
    49  		return nil, nil
    50  	}
    51  
    52  	// include a timeout to prevent an API call from
    53  	// hanging the build process indefinitely. The
    54  	// external service must return a request within
    55  	// one minute.
    56  	ctx, cancel := context.WithTimeout(ctx, time.Minute)
    57  	defer cancel()
    58  
    59  	req := &secret.Request{
    60  		Name:  name,
    61  		Path:  path,
    62  		Repo:  *in.Repo,
    63  		Build: *in.Build,
    64  	}
    65  	res, err := p.client.Find(ctx, req)
    66  	if err != nil {
    67  		logger.WithError(err).Debug("secret: external: cannot get secret")
    68  		return nil, err
    69  	}
    70  
    71  	// if no error is returned and the secret is empty,
    72  	// this indicates the client returned No Content,
    73  	// and we should exit with no secret, but no error.
    74  	if res.Data == "" {
    75  		logger.Trace("secret: external: secret is empty")
    76  		return nil, nil
    77  	}
    78  
    79  	logger.Trace("secret: external: found matching secret")
    80  
    81  	return &drone.Secret{
    82  		Name:        in.Name,
    83  		Data:        res.Data,
    84  		PullRequest: res.Pull,
    85  	}, nil
    86  }
    87  
    88  func getExternal(spec *manifest.Manifest, match string) (path, name string, ok bool) {
    89  	for _, resource := range spec.Resources {
    90  		secret, ok := resource.(*manifest.Secret)
    91  		if !ok {
    92  			continue
    93  		}
    94  		if secret.Name != match {
    95  			continue
    96  		}
    97  		if secret.Get.Name == "" && secret.Get.Path == "" {
    98  			continue
    99  		}
   100  		return secret.Get.Path, secret.Get.Name, true
   101  	}
   102  	return
   103  }