// 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. package registry import ( "context" "strings" "time" "github.com/drone/drone-go/plugin/secret" "github.com/drone/drone-yaml/yaml" "github.com/drone/drone/core" "github.com/drone/drone/plugin/registry/auths" droneapi "github.com/drone/drone-go/drone" ) // External returns a new external Secret controller. func External(endpoint, secret string, skipVerify bool) core.RegistryService { return &externalController{} } type externalController struct { endpoint string secret string skipVerify bool } func (c *externalController) List(ctx context.Context, in *core.RegistryArgs) ([]*core.Registry, error) { // 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) if !ok { 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 { 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 == "" { 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 { return nil, nil } return auths.ParseString(res.Data) } func getExternal(manifest *yaml.Manifest) (path, name string, ok bool) { for _, resource := range manifest.Resources { secret, ok := resource.(*yaml.Secret) if !ok { continue } if !strings.EqualFold(secret.Type, "docker") { continue } value, ok := secret.External["docker_auth_config"] if ok { return value.Path, value.Name, ok } value, ok = secret.External[".dockerconfig"] if ok { return value.Path, value.Name, ok } value, ok = secret.External[".dockerconfigjson"] if ok { return value.Path, value.Name, ok } } return } func toRepo(from *core.Repository) droneapi.Repo { return droneapi.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) droneapi.Build { return droneapi.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, } }