harness-drone/plugin/secret/external.go
2019-06-22 19:00:12 -07:00

175 lines
4.4 KiB
Go

// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package secret
import (
"context"
"time"
"github.com/drone/drone-yaml/yaml"
"github.com/drone/drone/core"
"github.com/drone/drone/logger"
"github.com/drone/drone-go/drone"
"github.com/drone/drone-go/plugin/secret"
)
// External returns a new external Secret controller.
func External(endpoint, secret string, skipVerify bool) core.SecretService {
return &externalController{
endpoint: endpoint,
secret: secret,
skipVerify: skipVerify,
}
}
type externalController struct {
endpoint string
secret string
skipVerify bool
}
func (c *externalController) Find(ctx context.Context, in *core.SecretArgs) (*core.Secret, error) {
if c.endpoint == "" {
return nil, nil
}
logger := logger.FromContext(ctx).
WithField("name", in.Name).
WithField("kind", "secret")
// lookup the named secret in the manifest. If the
// secret does not exist, return a nil variable,
// allowing the next secret controller in the chain
// to be invoked.
path, name, ok := getExternal(in.Conf, in.Name)
if !ok {
logger.Trace("secret: external: no matching secret")
return nil, nil
}
// 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 := &secret.Request{
Name: name,
Path: path,
Repo: toRepo(in.Repo),
Build: toBuild(in.Build),
}
client := secret.Client(c.endpoint, c.secret, c.skipVerify)
res, err := client.Find(ctx, req)
if err != nil {
logger.WithError(err).Trace("secret: external: cannot get secret")
return nil, err
}
// if no error is returned and the secret is empty,
// this indicates the client returned No Content,
// and we should exit with no secret, but no error.
if res.Data == "" {
logger.Trace("secret: external: secret disabled for pull requests")
return nil, nil
}
// the secret can be restricted to non-pull request
// events. If the secret is restricted, return
// empty results.
if (res.Pull == false && res.PullRequest == false) &&
in.Build.Event == core.EventPullRequest {
logger.Trace("secret: external: restricted from forks")
return nil, nil
}
logger.Trace("secret: external: found matching secret")
return &core.Secret{
Name: in.Name,
Data: res.Data,
PullRequest: res.Pull,
}, nil
}
func getExternal(manifest *yaml.Manifest, match string) (path, name string, ok bool) {
for _, resource := range manifest.Resources {
secret, ok := resource.(*yaml.Secret)
if !ok {
continue
}
if secret.Name != match {
continue
}
if secret.Get.Name == "" && secret.Get.Path == "" {
continue
}
return secret.Get.Path, secret.Get.Name, true
}
return
}
func toRepo(from *core.Repository) drone.Repo {
return drone.Repo{
ID: from.ID,
UID: from.UID,
UserID: from.UserID,
Namespace: from.Namespace,
Name: from.Name,
Slug: from.Slug,
SCM: from.SCM,
HTTPURL: from.HTTPURL,
SSHURL: from.SSHURL,
Link: from.Link,
Branch: from.Branch,
Private: from.Private,
Visibility: from.Visibility,
Active: from.Active,
Config: from.Config,
Trusted: from.Trusted,
Protected: from.Protected,
Timeout: from.Timeout,
}
}
func toBuild(from *core.Build) drone.Build {
return drone.Build{
ID: from.ID,
RepoID: from.RepoID,
Trigger: from.Trigger,
Number: from.Number,
Parent: from.Parent,
Status: from.Status,
Error: from.Error,
Event: from.Event,
Action: from.Action,
Link: from.Link,
Timestamp: from.Timestamp,
Title: from.Title,
Message: from.Message,
Before: from.Before,
After: from.After,
Ref: from.Ref,
Fork: from.Fork,
Source: from.Source,
Target: from.Target,
Author: from.Author,
AuthorName: from.AuthorName,
AuthorEmail: from.AuthorEmail,
AuthorAvatar: from.AuthorAvatar,
Sender: from.Sender,
Params: from.Params,
Deploy: from.Deploy,
Started: from.Started,
Finished: from.Finished,
Created: from.Created,
Updated: from.Updated,
Version: from.Version,
}
}