0
0
mirror of https://github.com/thegeeklab/wp-ansible.git synced 2024-11-21 23:50:40 +00:00

refactor: rewrite to use plugin boilerplate (#12)

* refactor: rewrite to use plugin boilerplate

* fix build source

* fix gitignore
This commit is contained in:
Robert Kaussow 2021-02-10 11:34:04 +01:00 committed by GitHub
parent 3436b3eb77
commit e80ca3a599
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 744 additions and 639 deletions

View File

@ -101,11 +101,11 @@ def testing(ctx):
def linux(ctx, arch): def linux(ctx, arch):
if ctx.build.event == "tag": if ctx.build.event == "tag":
build = [ build = [
'go build -v -ldflags "-X main.version=%s" -a -tags netgo -o release/linux/%s/drone-ansible .' % (ctx.build.ref.replace("refs/tags/v", ""), arch), 'go build -v -ldflags "-X main.version=%s" -a -tags netgo -o release/linux/%s/drone-ansible ./cmd/drone-ansible' % (ctx.build.ref.replace("refs/tags/v", ""), arch),
] ]
else: else:
build = [ build = [
'go build -v -ldflags "-X main.version=%s" -a -tags netgo -o release/linux/%s/drone-ansible .' % (ctx.build.commit[0:8], arch), 'go build -v -ldflags "-X main.version=%s" -a -tags netgo -o release/linux/%s/drone-ansible ./cmd/drone-ansible' % (ctx.build.commit[0:8], arch),
] ]
steps = [ steps = [

4
.gitignore vendored
View File

@ -1,5 +1,5 @@
release/ /release/
/drone-ansible*
coverage.out coverage.out
drone-ansible
.drone.yml .drone.yml

211
cmd/drone-ansible/config.go Normal file
View File

@ -0,0 +1,211 @@
package main
import (
"github.com/owncloud-ci/drone-ansible/plugin"
"github.com/urfave/cli/v2"
)
// settingsFlags has the cli.Flags for the plugin.Settings.
func settingsFlags(settings *plugin.Settings) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "requirements",
Usage: "path to python requirements",
EnvVars: []string{"PLUGIN_REQUIREMENTS"},
Destination: &settings.Requirements,
},
&cli.StringFlag{
Name: "galaxy",
Usage: "path to galaxy requirements",
EnvVars: []string{"PLUGIN_GALAXY"},
Destination: &settings.Galaxy,
},
&cli.StringSliceFlag{
Name: "inventory",
Usage: "specify inventory host path",
EnvVars: []string{"PLUGIN_INVENTORY", "PLUGIN_INVENTORIES"},
Destination: &settings.Inventories,
},
&cli.StringSliceFlag{
Name: "playbook",
Usage: "list of playbooks to apply",
EnvVars: []string{"PLUGIN_PLAYBOOK", "PLUGIN_PLAYBOOKS"},
Destination: &settings.Playbooks,
},
&cli.StringFlag{
Name: "limit",
Usage: "further limit selected hosts to an additional pattern",
EnvVars: []string{"PLUGIN_LIMIT"},
Destination: &settings.Limit,
},
&cli.StringFlag{
Name: "skip-tags",
Usage: "only run plays and tasks whose tags do not match",
EnvVars: []string{"PLUGIN_SKIP_TAGS"},
Destination: &settings.SkipTags,
},
&cli.StringFlag{
Name: "start-at-task",
Usage: "start the playbook at the task matching this name",
EnvVars: []string{"PLUGIN_START_AT_TASK"},
Destination: &settings.StartAtTask,
},
&cli.StringFlag{
Name: "tags",
Usage: "only run plays and tasks tagged with these values",
EnvVars: []string{"PLUGIN_TAGS"},
Destination: &settings.Tags,
},
&cli.StringSliceFlag{
Name: "extra-vars",
Usage: "set additional variables as key=value",
EnvVars: []string{"PLUGIN_EXTRA_VARS", "ANSIBLE_EXTRA_VARS"},
Destination: &settings.ExtraVars,
},
&cli.StringSliceFlag{
Name: "module-path",
Usage: "prepend paths to module library",
EnvVars: []string{"PLUGIN_MODULE_PATH"},
Destination: &settings.ModulePath,
},
&cli.BoolFlag{
Name: "check",
Usage: "run a check, do not apply any changes",
EnvVars: []string{"PLUGIN_CHECK"},
Destination: &settings.Check,
},
&cli.BoolFlag{
Name: "diff",
Usage: "show the differences, may print secrets",
EnvVars: []string{"PLUGIN_DIFF"},
Destination: &settings.Diff,
},
&cli.BoolFlag{
Name: "flush-cache",
Usage: "clear the fact cache for every host in inventory",
EnvVars: []string{"PLUGIN_FLUSH_CACHE"},
Destination: &settings.FlushCache,
},
&cli.BoolFlag{
Name: "force-handlers",
Usage: "run handlers even if a task fails",
EnvVars: []string{"PLUGIN_FORCE_HANDLERS"},
Destination: &settings.ForceHandlers,
},
&cli.BoolFlag{
Name: "list-hosts",
Usage: "outputs a list of matching hosts",
EnvVars: []string{"PLUGIN_LIST_HOSTS"},
Destination: &settings.ListHosts,
},
&cli.BoolFlag{
Name: "list-tags",
Usage: "list all available tags",
EnvVars: []string{"PLUGIN_LIST_TAGS"},
Destination: &settings.ListTags,
},
&cli.BoolFlag{
Name: "list-tasks",
Usage: "list all tasks that would be executed",
EnvVars: []string{"PLUGIN_LIST_TASKS"},
Destination: &settings.ListTasks,
},
&cli.BoolFlag{
Name: "syntax-check",
Usage: "perform a syntax check on the playbook",
EnvVars: []string{"PLUGIN_SYNTAX_CHECK"},
Destination: &settings.SyntaxCheck,
},
&cli.IntFlag{
Name: "forks",
Usage: "specify number of parallel processes to use",
EnvVars: []string{"PLUGIN_FORKS"},
Value: 5,
Destination: &settings.Forks,
},
&cli.StringFlag{
Name: "vault-id",
Usage: "the vault identity to use",
EnvVars: []string{"PLUGIN_VAULT_ID", "ANSIBLE_VAULT_ID"},
Destination: &settings.VaultID,
},
&cli.StringFlag{
Name: "vault-password",
Usage: "the vault password to use",
EnvVars: []string{"PLUGIN_VAULT_PASSWORD", "ANSIBLE_VAULT_PASSWORD"},
Destination: &settings.VaultPassword,
},
&cli.IntFlag{
Name: "verbose",
Usage: "level of verbosity, 0 up to 4",
EnvVars: []string{"PLUGIN_VERBOSE"},
Destination: &settings.Verbose,
},
&cli.StringFlag{
Name: "private-key",
Usage: "use this key to authenticate the connection",
EnvVars: []string{"PLUGIN_PRIVATE_KEY", "ANSIBLE_PRIVATE_KEY"},
Destination: &settings.PrivateKey,
},
&cli.StringFlag{
Name: "user",
Usage: "connect as this user",
EnvVars: []string{"PLUGIN_USER", "ANSIBLE_USER"},
Destination: &settings.User,
},
&cli.StringFlag{
Name: "connection",
Usage: "connection type to use",
EnvVars: []string{"PLUGIN_CONNECTION"},
Destination: &settings.Connection,
},
&cli.IntFlag{
Name: "timeout",
Usage: "override the connection timeout in seconds",
EnvVars: []string{"PLUGIN_TIMEOUT"},
Destination: &settings.Timeout,
},
&cli.StringFlag{
Name: "ssh-common-args",
Usage: "specify common arguments to pass to sftp/scp/ssh",
EnvVars: []string{"PLUGIN_SSH_COMMON_ARGS"},
Destination: &settings.SSHCommonArgs,
},
&cli.StringFlag{
Name: "sftp-extra-args",
Usage: "specify extra arguments to pass to sftp only",
EnvVars: []string{"PLUGIN_SFTP_EXTRA_ARGS"},
Destination: &settings.SFTPExtraArgs,
},
&cli.StringFlag{
Name: "scp-extra-args",
Usage: "specify extra arguments to pass to scp only",
EnvVars: []string{"PLUGIN_SCP_EXTRA_ARGS"},
Destination: &settings.SCPExtraArgs,
},
&cli.StringFlag{
Name: "ssh-extra-args",
Usage: "specify extra arguments to pass to ssh only",
EnvVars: []string{"PLUGIN_SSH_EXTRA_ARGS"},
Destination: &settings.SSHExtraArgs,
},
&cli.BoolFlag{
Name: "become",
Usage: "run operations with become",
EnvVars: []string{"PLUGIN_BECOME"},
Destination: &settings.Become,
},
&cli.StringFlag{
Name: "become-method",
Usage: "privilege escalation method to use",
EnvVars: []string{"PLUGIN_BECOME_METHOD", "ANSIBLE_BECOME_METHOD"},
Destination: &settings.BecomeMethod,
},
&cli.StringFlag{
Name: "become-user",
Usage: "run operations as this user",
EnvVars: []string{"PLUGIN_BECOME_USER", "ANSIBLE_BECOME_USER"},
Destination: &settings.BecomeUser,
},
}
}

64
cmd/drone-ansible/main.go Normal file
View File

@ -0,0 +1,64 @@
package main
import (
"os"
"github.com/joho/godotenv"
"github.com/owncloud-ci/drone-ansible/plugin"
"github.com/urfave/cli/v2"
"github.com/drone-plugins/drone-plugin-lib/errors"
"github.com/drone-plugins/drone-plugin-lib/urfave"
)
var version = "unknown"
func main() {
settings := &plugin.Settings{}
if _, err := os.Stat("/run/drone/env"); err == nil {
godotenv.Overload("/run/drone/env")
}
app := &cli.App{
Name: "drone-ansible",
Usage: "provision infrastructure with Ansible",
Version: version,
Flags: append(settingsFlags(settings), urfave.Flags()...),
Action: run(settings),
}
if err := app.Run(os.Args); err != nil {
errors.HandleExit(err)
}
}
func run(settings *plugin.Settings) cli.ActionFunc {
return func(ctx *cli.Context) error {
urfave.LoggingFromContext(ctx)
plugin := plugin.New(
*settings,
urfave.PipelineFromContext(ctx),
urfave.NetworkFromContext(ctx),
)
if err := plugin.Validate(); err != nil {
if e, ok := err.(errors.ExitCoder); ok {
return e
}
return errors.ExitMessagef("validation failed: %w", err)
}
if err := plugin.Execute(); err != nil {
if e, ok := err.(errors.ExitCoder); ok {
return e
}
return errors.ExitMessagef("execution failed: %w", err)
}
return nil
}
}

6
go.mod
View File

@ -4,7 +4,11 @@ go 1.15
require ( require (
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/drone-plugins/drone-plugin-lib v0.4.0
github.com/joho/godotenv v1.3.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/urfave/cli v1.22.5 github.com/sirupsen/logrus v1.7.0 // indirect
github.com/urfave/cli/v2 v2.3.0
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
) )

36
go.sum
View File

@ -4,8 +4,19 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSY
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/drone-plugins/drone-plugin-lib v0.4.0 h1:qywEYGhquUuid6zNLmKia8CWY1TUa8jPQQ/G9ozfAmc=
github.com/drone-plugins/drone-plugin-lib v0.4.0/go.mod h1:EgqogX38GoJFtckeSQyhBJYX8P+KWBPhdprAVvyRxF8=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -13,7 +24,28 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=

243
main.go
View File

@ -1,243 +0,0 @@
package main
import (
"log"
"os"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
version = "unknown"
)
func main() {
app := cli.NewApp()
app.Name = "ansible plugin"
app.Usage = "ansible plugin"
app.Action = run
app.Version = version
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "requirements",
Usage: "path to python requirements",
EnvVar: "PLUGIN_REQUIREMENTS",
},
cli.StringFlag{
Name: "galaxy",
Usage: "path to galaxy requirements",
EnvVar: "PLUGIN_GALAXY",
},
cli.StringSliceFlag{
Name: "inventory",
Usage: "specify inventory host path",
EnvVar: "PLUGIN_INVENTORY,PLUGIN_INVENTORIES",
},
cli.StringSliceFlag{
Name: "playbook",
Usage: "list of playbooks to apply",
EnvVar: "PLUGIN_PLAYBOOK,PLUGIN_PLAYBOOKS",
},
cli.StringFlag{
Name: "limit",
Usage: "further limit selected hosts to an additional pattern",
EnvVar: "PLUGIN_LIMIT",
},
cli.StringFlag{
Name: "skip-tags",
Usage: "only run plays and tasks whose tags do not match",
EnvVar: "PLUGIN_SKIP_TAGS",
},
cli.StringFlag{
Name: "start-at-task",
Usage: "start the playbook at the task matching this name",
EnvVar: "PLUGIN_START_AT_TASK",
},
cli.StringFlag{
Name: "tags",
Usage: "only run plays and tasks tagged with these values",
EnvVar: "PLUGIN_TAGS",
},
cli.StringSliceFlag{
Name: "extra-vars",
Usage: "set additional variables as key=value",
EnvVar: "PLUGIN_EXTRA_VARS,ANSIBLE_EXTRA_VARS",
},
cli.StringSliceFlag{
Name: "module-path",
Usage: "prepend paths to module library",
EnvVar: "PLUGIN_MODULE_PATH",
},
cli.BoolFlag{
Name: "check",
Usage: "run a check, do not apply any changes",
EnvVar: "PLUGIN_CHECK",
},
cli.BoolFlag{
Name: "diff",
Usage: "show the differences, may print secrets",
EnvVar: "PLUGIN_DIFF",
},
cli.BoolFlag{
Name: "flush-cache",
Usage: "clear the fact cache for every host in inventory",
EnvVar: "PLUGIN_FLUSH_CACHE",
},
cli.BoolFlag{
Name: "force-handlers",
Usage: "run handlers even if a task fails",
EnvVar: "PLUGIN_FORCE_HANDLERS",
},
cli.BoolFlag{
Name: "list-hosts",
Usage: "outputs a list of matching hosts",
EnvVar: "PLUGIN_LIST_HOSTS",
},
cli.BoolFlag{
Name: "list-tags",
Usage: "list all available tags",
EnvVar: "PLUGIN_LIST_TAGS",
},
cli.BoolFlag{
Name: "list-tasks",
Usage: "list all tasks that would be executed",
EnvVar: "PLUGIN_LIST_TASKS",
},
cli.BoolFlag{
Name: "syntax-check",
Usage: "perform a syntax check on the playbook",
EnvVar: "PLUGIN_SYNTAX_CHECK",
},
cli.IntFlag{
Name: "forks",
Usage: "specify number of parallel processes to use",
EnvVar: "PLUGIN_FORKS",
Value: 5,
},
cli.StringFlag{
Name: "vault-id",
Usage: "the vault identity to use",
EnvVar: "PLUGIN_VAULT_ID,ANSIBLE_VAULT_ID",
},
cli.StringFlag{
Name: "vault-password",
Usage: "the vault password to use",
EnvVar: "PLUGIN_VAULT_PASSWORD,ANSIBLE_VAULT_PASSWORD",
},
cli.IntFlag{
Name: "verbose",
Usage: "level of verbosity, 0 up to 4",
EnvVar: "PLUGIN_VERBOSE",
},
cli.StringFlag{
Name: "private-key",
Usage: "use this key to authenticate the connection",
EnvVar: "PLUGIN_PRIVATE_KEY,ANSIBLE_PRIVATE_KEY",
},
cli.StringFlag{
Name: "user",
Usage: "connect as this user",
EnvVar: "PLUGIN_USER,ANSIBLE_USER",
},
cli.StringFlag{
Name: "connection",
Usage: "connection type to use",
EnvVar: "PLUGIN_CONNECTION",
},
cli.IntFlag{
Name: "timeout",
Usage: "override the connection timeout in seconds",
EnvVar: "PLUGIN_TIMEOUT",
},
cli.StringFlag{
Name: "ssh-common-args",
Usage: "specify common arguments to pass to sftp/scp/ssh",
EnvVar: "PLUGIN_SSH_COMMON_ARGS",
},
cli.StringFlag{
Name: "sftp-extra-args",
Usage: "specify extra arguments to pass to sftp only",
EnvVar: "PLUGIN_SFTP_EXTRA_ARGS",
},
cli.StringFlag{
Name: "scp-extra-args",
Usage: "specify extra arguments to pass to scp only",
EnvVar: "PLUGIN_SCP_EXTRA_ARGS",
},
cli.StringFlag{
Name: "ssh-extra-args",
Usage: "specify extra arguments to pass to ssh only",
EnvVar: "PLUGIN_SSH_EXTRA_ARGS",
},
cli.BoolFlag{
Name: "become",
Usage: "run operations with become",
EnvVar: "PLUGIN_BECOME",
},
cli.StringFlag{
Name: "become-method",
Usage: "privilege escalation method to use",
EnvVar: "PLUGIN_BECOME_METHOD,ANSIBLE_BECOME_METHOD",
},
cli.StringFlag{
Name: "become-user",
Usage: "run operations as this user",
EnvVar: "PLUGIN_BECOME_USER,ANSIBLE_BECOME_USER",
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
func run(c *cli.Context) error {
plugin := Plugin{
Config: Config{
Requirements: c.String("requirements"),
Galaxy: c.String("galaxy"),
Inventories: c.StringSlice("inventory"),
Playbooks: c.StringSlice("playbook"),
Limit: c.String("limit"),
SkipTags: c.String("skip-tags"),
StartAtTask: c.String("start-at-task"),
Tags: c.String("tags"),
ExtraVars: c.StringSlice("extra-vars"),
ModulePath: c.StringSlice("module-path"),
Check: c.Bool("check"),
Diff: c.Bool("diff"),
FlushCache: c.Bool("flush-cache"),
ForceHandlers: c.Bool("force-handlers"),
ListHosts: c.Bool("list-hosts"),
ListTags: c.Bool("list-tags"),
ListTasks: c.Bool("list-tasks"),
SyntaxCheck: c.Bool("syntax-check"),
Forks: c.Int("forks"),
VaultID: c.String("vailt-id"),
VaultPassword: c.String("vault-password"),
Verbose: c.Int("verbose"),
PrivateKey: c.String("private-key"),
User: c.String("user"),
Connection: c.String("connection"),
Timeout: c.Int("timeout"),
SSHCommonArgs: c.String("ssh-common-args"),
SFTPExtraArgs: c.String("sftp-extra-args"),
SCPExtraArgs: c.String("scp-extra-args"),
SSHExtraArgs: c.String("ssh-extra-args"),
Become: c.Bool("become"),
BecomeMethod: c.String("become-method"),
BecomeUser: c.String("become-user"),
},
}
if len(plugin.Config.Playbooks) == 0 {
return errors.New("you must provide a playbook")
}
if len(plugin.Config.Inventories) == 0 {
return errors.New("you must provide an inventory")
}
return plugin.Exec()
}

389
plugin.go
View File

@ -1,389 +0,0 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"github.com/pkg/errors"
)
var ansibleFolder = "/etc/ansible"
var ansibleConfig = "/etc/ansible/ansible.cfg"
var ansibleContent = `
[defaults]
host_key_checking = False
`
type (
// Config for the plugin.
Config struct {
Requirements string
Galaxy string
Inventories []string
Playbooks []string
Limit string
SkipTags string
StartAtTask string
Tags string
ExtraVars []string
ModulePath []string
Check bool
Diff bool
FlushCache bool
ForceHandlers bool
ListHosts bool
ListTags bool
ListTasks bool
SyntaxCheck bool
Forks int
VaultID string
VaultPassword string
VaultPasswordFile string
Verbose int
PrivateKey string
PrivateKeyFile string
User string
Connection string
Timeout int
SSHCommonArgs string
SFTPExtraArgs string
SCPExtraArgs string
SSHExtraArgs string
Become bool
BecomeMethod string
BecomeUser string
}
// Plugin definition.
Plugin struct {
Config Config
}
)
// Exec provides the implementation of the plugin.
func (p *Plugin) Exec() error {
if err := p.playbooks(); err != nil {
return err
}
if err := p.ansibleConfig(); err != nil {
return err
}
if p.Config.PrivateKey != "" {
if err := p.privateKey(); err != nil {
return err
}
defer os.Remove(p.Config.PrivateKeyFile)
}
if p.Config.VaultPassword != "" {
if err := p.vaultPass(); err != nil {
return err
}
defer os.Remove(p.Config.VaultPasswordFile)
}
commands := []*exec.Cmd{
p.versionCommand(),
}
if p.Config.Requirements != "" {
commands = append(commands, p.requirementsCommand())
}
if p.Config.Galaxy != "" {
commands = append(commands, p.galaxyCommand())
}
for _, inventory := range p.Config.Inventories {
commands = append(commands, p.ansibleCommand(inventory))
}
for _, cmd := range commands {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "ANSIBLE_FORCE_COLOR=1")
trace(cmd)
if err := cmd.Run(); err != nil {
return err
}
}
return nil
}
func (p *Plugin) ansibleConfig() error {
if err := os.MkdirAll(ansibleFolder, os.ModePerm); err != nil {
return errors.Wrap(err, "failed to create ansible directory")
}
if err := ioutil.WriteFile(ansibleConfig, []byte(ansibleContent), 0600); err != nil {
return errors.Wrap(err, "failed to create ansible config")
}
return nil
}
func (p *Plugin) privateKey() error {
tmpfile, err := ioutil.TempFile("", "privateKey")
if err != nil {
return errors.Wrap(err, "failed to create private key file")
}
if _, err := tmpfile.Write([]byte(p.Config.PrivateKey)); err != nil {
return errors.Wrap(err, "failed to write private key file")
}
if err := tmpfile.Close(); err != nil {
return errors.Wrap(err, "failed to close private key file")
}
p.Config.PrivateKeyFile = tmpfile.Name()
return nil
}
func (p *Plugin) vaultPass() error {
tmpfile, err := ioutil.TempFile("", "vaultPass")
if err != nil {
return errors.Wrap(err, "failed to create vault password file")
}
if _, err := tmpfile.Write([]byte(p.Config.VaultPassword)); err != nil {
return errors.Wrap(err, "failed to write vault password file")
}
if err := tmpfile.Close(); err != nil {
return errors.Wrap(err, "failed to close vault password file")
}
p.Config.VaultPasswordFile = tmpfile.Name()
return nil
}
func (p *Plugin) playbooks() error {
var (
playbooks []string
)
for _, p := range p.Config.Playbooks {
files, err := filepath.Glob(p)
if err != nil {
playbooks = append(playbooks, p)
continue
}
playbooks = append(playbooks, files...)
}
if len(playbooks) == 0 {
return errors.New("failed to find playbook files")
}
p.Config.Playbooks = playbooks
return nil
}
func (p *Plugin) versionCommand() *exec.Cmd {
args := []string{
"--version",
}
return exec.Command(
"ansible",
args...,
)
}
func (p *Plugin) requirementsCommand() *exec.Cmd {
args := []string{
"install",
"--upgrade",
"--requirement",
p.Config.Requirements,
}
return exec.Command(
"pip",
args...,
)
}
func (p *Plugin) galaxyCommand() *exec.Cmd {
args := []string{
"install",
"--force",
"--role-file",
p.Config.Galaxy,
}
if p.Config.Verbose > 0 {
args = append(args, fmt.Sprintf("-%s", strings.Repeat("v", p.Config.Verbose)))
}
return exec.Command(
"ansible-galaxy",
args...,
)
}
func (p *Plugin) ansibleCommand(inventory string) *exec.Cmd {
args := []string{
"--inventory",
inventory,
}
if len(p.Config.ModulePath) > 0 {
args = append(args, "--module-path", strings.Join(p.Config.ModulePath, ":"))
}
if p.Config.VaultID != "" {
args = append(args, "--vault-id", p.Config.VaultID)
}
if p.Config.VaultPasswordFile != "" {
args = append(args, "--vault-password-file", p.Config.VaultPasswordFile)
}
for _, v := range p.Config.ExtraVars {
args = append(args, "--extra-vars", v)
}
if p.Config.ListHosts {
args = append(args, "--list-hosts")
args = append(args, p.Config.Playbooks...)
return exec.Command(
"ansible-playbook",
args...,
)
}
if p.Config.SyntaxCheck {
args = append(args, "--syntax-check")
args = append(args, p.Config.Playbooks...)
return exec.Command(
"ansible-playbook",
args...,
)
}
if p.Config.Check {
args = append(args, "--check")
}
if p.Config.Diff {
args = append(args, "--diff")
}
if p.Config.FlushCache {
args = append(args, "--flush-cache")
}
if p.Config.ForceHandlers {
args = append(args, "--force-handlers")
}
if p.Config.Forks != 5 {
args = append(args, "--forks", strconv.Itoa(p.Config.Forks))
}
if p.Config.Limit != "" {
args = append(args, "--limit", p.Config.Limit)
}
if p.Config.ListTags {
args = append(args, "--list-tags")
}
if p.Config.ListTasks {
args = append(args, "--list-tasks")
}
if p.Config.SkipTags != "" {
args = append(args, "--skip-tags", p.Config.SkipTags)
}
if p.Config.StartAtTask != "" {
args = append(args, "--start-at-task", p.Config.StartAtTask)
}
if p.Config.Tags != "" {
args = append(args, "--tags", p.Config.Tags)
}
if p.Config.PrivateKeyFile != "" {
args = append(args, "--private-key", p.Config.PrivateKeyFile)
}
if p.Config.User != "" {
args = append(args, "--user", p.Config.User)
}
if p.Config.Connection != "" {
args = append(args, "--connection", p.Config.Connection)
}
if p.Config.Timeout != 0 {
args = append(args, "--timeout", strconv.Itoa(p.Config.Timeout))
}
if p.Config.SSHCommonArgs != "" {
args = append(args, "--ssh-common-args", p.Config.SSHCommonArgs)
}
if p.Config.SFTPExtraArgs != "" {
args = append(args, "--sftp-extra-args", p.Config.SFTPExtraArgs)
}
if p.Config.SCPExtraArgs != "" {
args = append(args, "--scp-extra-args", p.Config.SCPExtraArgs)
}
if p.Config.SSHExtraArgs != "" {
args = append(args, "--ssh-extra-args", p.Config.SSHExtraArgs)
}
if p.Config.Become {
args = append(args, "--become")
}
if p.Config.BecomeMethod != "" {
args = append(args, "--become-method", p.Config.BecomeMethod)
}
if p.Config.BecomeUser != "" {
args = append(args, "--become-user", p.Config.BecomeUser)
}
if p.Config.Verbose > 0 {
args = append(args, fmt.Sprintf("-%s", strings.Repeat("v", p.Config.Verbose)))
}
args = append(args, p.Config.Playbooks...)
return exec.Command(
"ansible-playbook",
args...,
)
}
func trace(cmd *exec.Cmd) {
fmt.Println("$", strings.Join(cmd.Args, " "))
}

285
plugin/ansible.go Normal file
View File

@ -0,0 +1,285 @@
package plugin
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
)
var ansibleFolder = "/etc/ansible"
var ansibleConfig = "/etc/ansible/ansible.cfg"
var ansibleContent = `
[defaults]
host_key_checking = False
`
func (p *Plugin) ansibleConfig() error {
if err := os.MkdirAll(ansibleFolder, os.ModePerm); err != nil {
return errors.Wrap(err, "failed to create ansible directory")
}
if err := ioutil.WriteFile(ansibleConfig, []byte(ansibleContent), 0600); err != nil {
return errors.Wrap(err, "failed to create ansible config")
}
return nil
}
func (p *Plugin) privateKey() error {
tmpfile, err := ioutil.TempFile("", "privateKey")
if err != nil {
return errors.Wrap(err, "failed to create private key file")
}
if _, err := tmpfile.Write([]byte(p.settings.PrivateKey)); err != nil {
return errors.Wrap(err, "failed to write private key file")
}
if err := tmpfile.Close(); err != nil {
return errors.Wrap(err, "failed to close private key file")
}
p.settings.PrivateKeyFile = tmpfile.Name()
return nil
}
func (p *Plugin) vaultPass() error {
tmpfile, err := ioutil.TempFile("", "vaultPass")
if err != nil {
return errors.Wrap(err, "failed to create vault password file")
}
if _, err := tmpfile.Write([]byte(p.settings.VaultPassword)); err != nil {
return errors.Wrap(err, "failed to write vault password file")
}
if err := tmpfile.Close(); err != nil {
return errors.Wrap(err, "failed to close vault password file")
}
p.settings.VaultPasswordFile = tmpfile.Name()
return nil
}
func (p *Plugin) playbooks() error {
var (
playbooks []string
)
for _, p := range p.settings.Playbooks.Value() {
files, err := filepath.Glob(p)
if err != nil {
playbooks = append(playbooks, p)
continue
}
playbooks = append(playbooks, files...)
}
if len(playbooks) == 0 {
return errors.New("failed to find playbook files")
}
p.settings.Playbooks = *cli.NewStringSlice(playbooks...)
return nil
}
func (p *Plugin) versionCommand() *exec.Cmd {
args := []string{
"--version",
}
return exec.Command(
"ansible",
args...,
)
}
func (p *Plugin) requirementsCommand() *exec.Cmd {
args := []string{
"install",
"--upgrade",
"--requirement",
p.settings.Requirements,
}
return exec.Command(
"pip",
args...,
)
}
func (p *Plugin) galaxyCommand() *exec.Cmd {
args := []string{
"install",
"--force",
"--role-file",
p.settings.Galaxy,
}
if p.settings.Verbose > 0 {
args = append(args, fmt.Sprintf("-%s", strings.Repeat("v", p.settings.Verbose)))
}
return exec.Command(
"ansible-galaxy",
args...,
)
}
func (p *Plugin) ansibleCommand(inventory string) *exec.Cmd {
args := []string{
"--inventory",
inventory,
}
if len(p.settings.ModulePath.Value()) > 0 {
args = append(args, "--module-path", strings.Join(p.settings.ModulePath.Value(), ":"))
}
if p.settings.VaultID != "" {
args = append(args, "--vault-id", p.settings.VaultID)
}
if p.settings.VaultPasswordFile != "" {
args = append(args, "--vault-password-file", p.settings.VaultPasswordFile)
}
for _, v := range p.settings.ExtraVars.Value() {
args = append(args, "--extra-vars", v)
}
if p.settings.ListHosts {
args = append(args, "--list-hosts")
args = append(args, p.settings.Playbooks.Value()...)
return exec.Command(
"ansible-playbook",
args...,
)
}
if p.settings.SyntaxCheck {
args = append(args, "--syntax-check")
args = append(args, p.settings.Playbooks.Value()...)
return exec.Command(
"ansible-playbook",
args...,
)
}
if p.settings.Check {
args = append(args, "--check")
}
if p.settings.Diff {
args = append(args, "--diff")
}
if p.settings.FlushCache {
args = append(args, "--flush-cache")
}
if p.settings.ForceHandlers {
args = append(args, "--force-handlers")
}
if p.settings.Forks != 5 {
args = append(args, "--forks", strconv.Itoa(p.settings.Forks))
}
if p.settings.Limit != "" {
args = append(args, "--limit", p.settings.Limit)
}
if p.settings.ListTags {
args = append(args, "--list-tags")
}
if p.settings.ListTasks {
args = append(args, "--list-tasks")
}
if p.settings.SkipTags != "" {
args = append(args, "--skip-tags", p.settings.SkipTags)
}
if p.settings.StartAtTask != "" {
args = append(args, "--start-at-task", p.settings.StartAtTask)
}
if p.settings.Tags != "" {
args = append(args, "--tags", p.settings.Tags)
}
if p.settings.PrivateKeyFile != "" {
args = append(args, "--private-key", p.settings.PrivateKeyFile)
}
if p.settings.User != "" {
args = append(args, "--user", p.settings.User)
}
if p.settings.Connection != "" {
args = append(args, "--connection", p.settings.Connection)
}
if p.settings.Timeout != 0 {
args = append(args, "--timeout", strconv.Itoa(p.settings.Timeout))
}
if p.settings.SSHCommonArgs != "" {
args = append(args, "--ssh-common-args", p.settings.SSHCommonArgs)
}
if p.settings.SFTPExtraArgs != "" {
args = append(args, "--sftp-extra-args", p.settings.SFTPExtraArgs)
}
if p.settings.SCPExtraArgs != "" {
args = append(args, "--scp-extra-args", p.settings.SCPExtraArgs)
}
if p.settings.SSHExtraArgs != "" {
args = append(args, "--ssh-extra-args", p.settings.SSHExtraArgs)
}
if p.settings.Become {
args = append(args, "--become")
}
if p.settings.BecomeMethod != "" {
args = append(args, "--become-method", p.settings.BecomeMethod)
}
if p.settings.BecomeUser != "" {
args = append(args, "--become-user", p.settings.BecomeUser)
}
if p.settings.Verbose > 0 {
args = append(args, fmt.Sprintf("-%s", strings.Repeat("v", p.settings.Verbose)))
}
args = append(args, p.settings.Playbooks.Value()...)
return exec.Command(
"ansible-playbook",
args...,
)
}
func trace(cmd *exec.Cmd) {
fmt.Println("$", strings.Join(cmd.Args, " "))
}

120
plugin/impl.go Normal file
View File

@ -0,0 +1,120 @@
package plugin
import (
"os"
"os/exec"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
)
// Settings for the Plugin.
type Settings struct {
Requirements string
Galaxy string
Inventories cli.StringSlice
Playbooks cli.StringSlice
Limit string
SkipTags string
StartAtTask string
Tags string
ExtraVars cli.StringSlice
ModulePath cli.StringSlice
Check bool
Diff bool
FlushCache bool
ForceHandlers bool
ListHosts bool
ListTags bool
ListTasks bool
SyntaxCheck bool
Forks int
VaultID string
VaultPassword string
VaultPasswordFile string
Verbose int
PrivateKey string
PrivateKeyFile string
User string
Connection string
Timeout int
SSHCommonArgs string
SFTPExtraArgs string
SCPExtraArgs string
SSHExtraArgs string
Become bool
BecomeMethod string
BecomeUser string
}
// Validate handles the settings validation of the plugin.
func (p *Plugin) Validate() error {
if len(p.settings.Playbooks.Value()) == 0 {
return errors.New("you must provide a playbook")
}
if len(p.settings.Inventories.Value()) == 0 {
return errors.New("you must provide an inventory")
}
return nil
}
// Execute provides the implementation of the plugin.
func (p *Plugin) Execute() error {
if err := p.playbooks(); err != nil {
return err
}
if err := p.ansibleConfig(); err != nil {
return err
}
if p.settings.PrivateKey != "" {
if err := p.privateKey(); err != nil {
return err
}
defer os.Remove(p.settings.PrivateKeyFile)
}
if p.settings.VaultPassword != "" {
if err := p.vaultPass(); err != nil {
return err
}
defer os.Remove(p.settings.VaultPasswordFile)
}
commands := []*exec.Cmd{
p.versionCommand(),
}
if p.settings.Requirements != "" {
commands = append(commands, p.requirementsCommand())
}
if p.settings.Galaxy != "" {
commands = append(commands, p.galaxyCommand())
}
for _, inventory := range p.settings.Inventories.Value() {
commands = append(commands, p.ansibleCommand(inventory))
}
for _, cmd := range commands {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "ANSIBLE_FORCE_COLOR=1")
trace(cmd)
if err := cmd.Run(); err != nil {
return err
}
}
return nil
}

21
plugin/plugin.go Normal file
View File

@ -0,0 +1,21 @@
package plugin
import (
"github.com/drone-plugins/drone-plugin-lib/drone"
)
// Plugin implements drone.Plugin to provide the plugin implementation.
type Plugin struct {
settings Settings
pipeline drone.Pipeline
network drone.Network
}
// New initializes a plugin from the given Settings, Pipeline, and Network.
func New(settings Settings, pipeline drone.Pipeline, network drone.Network) drone.Plugin {
return &Plugin{
settings: settings,
pipeline: pipeline,
network: network,
}
}