From 7a51a48cd21a442d0db0630f0416388fea03a602 Mon Sep 17 00:00:00 2001 From: sebclem Date: Tue, 4 Jul 2023 16:38:22 +0200 Subject: [PATCH] go (#13) Co-authored-by: SebClem Reviewed-on: https://git.sebclem.fr/sebclem/woodpecker-ansible-runner/pulls/13 --- .woodpecker.yml | 7 +- Dockerfile | 20 ++++- README.md | 44 +++++----- entrypoint | 82 ------------------ entrypoint.go | 219 ++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 10 +++ go.sum | 18 ++++ old.yml | 57 ------------- 8 files changed, 291 insertions(+), 166 deletions(-) delete mode 100755 entrypoint create mode 100644 entrypoint.go create mode 100644 go.mod create mode 100644 go.sum delete mode 100644 old.yml diff --git a/.woodpecker.yml b/.woodpecker.yml index f99d2b4..a655360 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -6,6 +6,7 @@ pipeline: cache_from: git.sebclem.fr/sebclem/${CI_REPO_NAME} registry: git.sebclem.fr dry_run: true + auto_tag: true logins: - registry: https://git.sebclem.fr username: @@ -19,7 +20,7 @@ pipeline: image: woodpeckerci/plugin-docker-buildx settings: platforms: linux/amd64 - tag: latest + auto_tag: true repo: git.sebclem.fr/sebclem/${CI_REPO_NAME} cache_from: git.sebclem.fr/sebclem/${CI_REPO_NAME} logins: @@ -29,5 +30,5 @@ pipeline: password: from_secret: docker_token when: - environment: production - event: deployment + event: + - tag \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 6ceb28a..302e4be 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,24 @@ -FROM python:3.11.4-slim +FROM golang:1.20.5-alpine AS build-stage -RUN apt-get update && apt-get install -y git toilet \ - && rm -rf /var/lib/apt/lists/* +WORKDIR /app + +COPY go.mod go.sum ./ +RUN go mod download + +COPY *.go ./ + +RUN CGO_ENABLED=0 GOOS=linux go build -o /entrypoint + + +FROM python:3.11.4-slim AS runtime-stage + +# RUN apt-get update && apt-get install -y git toilet \ +# && rm -rf /var/lib/apt/lists/* # renovate: datasource=pypi depName=ansible ENV ANSIBLE_VERSION=8.1.0 RUN pip3 install --no-cache-dir ansible==${ANSIBLE_VERSION} dnspython passlib netaddr -COPY entrypoint /bin/entrypoint +COPY --from=build-stage /entrypoint /bin/entrypoint ENTRYPOINT [ "/bin/entrypoint" ] \ No newline at end of file diff --git a/README.md b/README.md index 389883e..114b464 100644 --- a/README.md +++ b/README.md @@ -6,39 +6,43 @@ kind: pipeline name: default steps: - - name: Check ansible syntax - image: harbor.sebclem.fr/sebclem/drone-ansible-runner + check-ansible-syntax: + image: git.sebclem.fr/sebclem/woodpecker-ansible-runner + pull: true settings: - playbook: sites.yml + playbook: playbooks/install.yml galaxy_file: roles/requirements.yml check_syntax: true vault_token: - from_secret: ansible_vault_password - private_key: - from_secret: ansible_private_key + from_secret: ansible_vault_token when: - event: - - push - - custom + - event: "push" + branch: [main, master] + - event: [pull_request, manual, deployment] - - name: Run ansible playbook - image: harbor.sebclem.fr/sebclem/drone-ansible-runner + run-ansible-playbook: + image: git.sebclem.fr/sebclem/woodpecker-ansible-runner + pull: true settings: - verbosity: ${verbosity=1} + verbosity: ${verbosity=0} + diff: ${diff=false} + check: ${check=false} limit: ${limit} tags: ${tags} - playbook: sites.yml + playbook: playbooks/install.yml galaxy_file: roles/requirements.yml - check_syntax: true vault_token: - from_secret: ansible_vault_password + from_secret: ansible_vault_token private_key: from_secret: ansible_private_key - limit: harbor.home when: - event: - - promote - - rollback - - custom + environment: production + event: deployment + +when: + - event: "push" + branch: [main, master] + - event: [pull_request, manual, deployment] + ``` \ No newline at end of file diff --git a/entrypoint b/entrypoint deleted file mode 100755 index 7d92f87..0000000 --- a/entrypoint +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash - -check=${PLUGIN_CHECK_SYNTAX:-false} -verbosity=${PLUGIN_VERBOSITY:-0} - -C_RESET="\e[39m" -C_RED="\e[31m" -C_YELLOW="\e[33m" -C_BLUE="\e[34m" - -run_command(){ - echo "➡️ $@" - $@ -} -echo -e "${C_BLUE}" -toilet -f smblock Woodpecker Ansible -toilet -f smblock " Runner" -echo -e "${C_RESET}" - -if [ -z "$PLUGIN_PLAYBOOK" ]; then - echo -e "⚠️ ${C_RED}'playbook' setting not defined, ABORT!${C_RESET}" - exit 1 -fi - -if [ -z "$PLUGIN_PRIVATE_KEY" ]; then - echo -e "⚠️ ${C_YELLOW}[WARN] 'private_key' setting not defined !${C_RESET}" -else - - run_command "mkdir /root/.ssh" - echo "$PLUGIN_PRIVATE_KEY" > /root/.ssh/id_ed25519 - run_command chmod 400 /root/.ssh/id_ed25519 -fi - -args=("$PLUGIN_PLAYBOOK") - -if [ -n "$PLUGIN_VAULT_TOKEN" ]; then - echo "💼 Adding vault token to 'credentials/ci_vault_token'" - run_command "mkdir credentials" - echo "$PLUGIN_VAULT_TOKEN" > credentials/ci_vault_token - args+=("--vault-password-file=credentials/ci_vault_token") - echo "⚙️ Clean ansible.cfg" - echo "➡️ sed -i '/vault_password_file.*/d' ./ansible.cfg" - sed -i '/vault_password_file.*/d' ./ansible.cfg - echo "" -fi - -if [ -f "ansible-ci.cfg" ]; then - echo "⚙️ ansible-ci.cfg is present, using it." - rm ansible.cfg - mv ansible-ci.cfg ansible.cfg - echo "" -fi - -if [ -n "$PLUGIN_GALAXY_FILE" ]; then - echo "🚀 Installing Galaxy dependencies ($PLUGIN_GALAXY_FILE)" - run_command "ansible-galaxy install -r $PLUGIN_GALAXY_FILE --force" - echo "" -fi - -if [[ $check = true ]]; then - args+=("--syntax-check") -fi - -if [[ $verbosity != "0" ]]; then - verb="-" - for i in `seq 1 $verbosity`; do - verb+="v" - done - args+=("$verb") -fi - -if [[ -n "$PLUGIN_LIMIT" ]]; then - args+=("--limit" "$PLUGIN_LIMIT") -fi - -if [[ -n "$PLUGIN_TAGS" ]]; then - args+=("--tags" "$PLUGIN_TAGS") -fi - -run_command "export ANSIBLE_HOST_KEY_CHECKING=False" -echo "➡️ ansible-playbook ${args[@]}" -ansible-playbook "${args[@]}" \ No newline at end of file diff --git a/entrypoint.go b/entrypoint.go new file mode 100644 index 0000000..6cfad50 --- /dev/null +++ b/entrypoint.go @@ -0,0 +1,219 @@ +package main + +import ( + "os" + "os/exec" + "strconv" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type pluginConfig struct { + check_syntax bool + check bool + diff bool + verbosity int64 + limit *string + tags *string + privateKey *string + vaultToken *string + galaxyFile *string + playbook *string +} + +func runCommand(sugar *zap.SugaredLogger, args ...string) { + sugar.Infof("➡️ %s", args) + cmd := exec.Command(args[0], args[1:]...) + cmd.Env = os.Environ() + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + os.Exit(1) + } +} + +func getPluginConfig(sugar *zap.SugaredLogger) pluginConfig { + config := pluginConfig{} + + check_syntax, present := os.LookupEnv("PLUGIN_CHECK_SYNTAX") + if !present { + config.check_syntax = false + } else { + converted, err := strconv.ParseBool(check_syntax) + if err != nil { + sugar.Fatal(err) + } + config.check_syntax = converted + } + + check, present := os.LookupEnv("PLUGIN_CHECK") + if !present { + config.check = false + } else { + converted, err := strconv.ParseBool(check) + if err != nil { + sugar.Fatal(err) + } + config.check = converted + } + + diff, present := os.LookupEnv("PLUGIN_DIFF") + if !present { + config.diff = false + } else { + converted, err := strconv.ParseBool(diff) + if err != nil { + sugar.Fatal(err) + } + config.diff = converted + } + + verbosity, present := os.LookupEnv("PLUGIN_VERBOSITY") + if !present { + config.verbosity = 0 + } else { + converted, err := strconv.ParseInt(verbosity, 10, 0) + if err != nil { + sugar.Fatal(err) + } + config.verbosity = converted + } + + limit, present := os.LookupEnv("PLUGIN_LIMIT") + if !present { + config.limit = nil + } else { + config.limit = &limit + } + + tags, present := os.LookupEnv("PLUGIN_TAGS") + if !present { + config.tags = nil + } else { + config.tags = &tags + } + + privateKey, present := os.LookupEnv("PLUGIN_PRIVATE_KEY") + if !present { + sugar.Warn("⚠️ 'private_key' setting not defined !") + config.privateKey = nil + } else { + config.privateKey = &privateKey + } + + vaultToken, present := os.LookupEnv("PLUGIN_VAULT_TOKEN") + if !present { + config.vaultToken = nil + } else { + config.vaultToken = &vaultToken + } + + galaxyFile, present := os.LookupEnv("PLUGIN_GALAXY_FILE") + if !present { + config.galaxyFile = nil + } else { + config.galaxyFile = &galaxyFile + } + + playbook, present := os.LookupEnv("PLUGIN_PLAYBOOK") + if !present { + sugar.Fatal("⚠️ 'playbook' setting not defined, ABORT!") + } else { + config.playbook = &playbook + } + + return config +} + +func main() { + // encoderConfig := zapcore.EncoderConfig{ + // MessageKey: "message", + // LevelKey: "level", + // EncodeLevel: zapcore.CapitalColorLevelEncoder, + // EncodeDuration: zapcore.StringDurationEncoder, + // EncodeCaller: zapcore.ShortCallerEncoder, + // } + // encoder := zapcore.NewConsoleEncoder(encoderConfig) + // core := zapcore.NewCore(encoder, os.Stdout, zapcore.InfoLevel) + config := zap.NewProductionConfig() + config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + config.EncoderConfig.TimeKey = "" + config.Encoding = "console" + config.DisableCaller = true + config.DisableStacktrace = true + logger, _ := config.Build() + defer logger.Sync() + sugar := logger.Sugar() + + sugar.Info("Woodpecker Ansible Runner") + pluginConfig := getPluginConfig(sugar) + + if pluginConfig.privateKey != nil { + runCommand(sugar, "mkdir", "/root/.ssh") + err := os.WriteFile("/root/.ssh/id_ed25519", []byte(*pluginConfig.privateKey), 0400) + if err != nil { + sugar.Fatal(err) + } + } + + args := []string{*pluginConfig.playbook} + + if pluginConfig.vaultToken != nil { + sugar.Info("💼 Adding vault token to 'credentials/ci_vault_token'") + runCommand(sugar, "mkdir", "credentials") + err := os.WriteFile("credentials/ci_vault_token", []byte(*pluginConfig.vaultToken), 0644) + if err != nil { + sugar.Fatal(err) + } + + args = append(args, "--vault-password-file=credentials/ci_vault_token") + + sugar.Info("⚙️ Clean ansible.cfg") + runCommand(sugar, "sed", "-i", "/vault_password_file.*/d", "./ansible.cfg") + } + + if _, err := os.Stat("ansible-ci.cfg"); err == nil { + sugar.Info("⚙️ ansible-ci.cfg is present, using it.") + os.Remove("ansible.cfg") + os.Rename("ansible-ci.cfg", "ansible.cfg") + } + + if pluginConfig.galaxyFile != nil { + sugar.Infof("🚀 Installing Galaxy dependencies (%s)", *pluginConfig.galaxyFile) + runCommand(sugar, "ansible-galaxy", "install", "-r", *pluginConfig.galaxyFile, "--force") + } + + if pluginConfig.check_syntax { + args = append(args, "--syntax-check") + } + + if pluginConfig.check { + args = append(args, "--check") + } + + if pluginConfig.diff { + args = append(args, "--diff") + } + + if pluginConfig.verbosity != 0 { + verb := "-" + for i := 0; i < int(pluginConfig.verbosity); i++ { + verb += "v" + } + args = append(args, verb) + } + + if pluginConfig.limit != nil { + args = append(args, "--limit", *pluginConfig.limit) + } + + if pluginConfig.tags != nil { + args = append(args, "--tags", *pluginConfig.tags) + } + + // runCommand(sugar, "export", "ANSIBLE_HOST_KEY_CHECKING=False") + command := append([]string{"ansible-playbook"}, args...) + runCommand(sugar, command...) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0d2cd68 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module woodpecker-ansible-runner + +go 1.20 + +require go.uber.org/zap v1.24.0 + +require ( + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..017cf3a --- /dev/null +++ b/go.sum @@ -0,0 +1,18 @@ +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/old.yml b/old.yml deleted file mode 100644 index d8e0057..0000000 --- a/old.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Build Docker -kind: pipeline -type: docker -steps: - - - name: Only build image - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: harbor.sebclem.fr/sebclem/drone-ansible-runner - cache_from: harbor.sebclem.fr/sebclem/drone-ansible-runner - registry: harbor.sebclem.fr - tags: latest - dry_run: true - when: - target: - exclude: - - production - - - name: Build and Push docker - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: harbor.sebclem.fr/sebclem/drone-ansible-runner - cache_from: harbor.sebclem.fr/sebclem/drone-ansible-runner - registry: harbor.sebclem.fr - tags: latest - when: - target: - - production - - - name: Notify - image: drillster/drone-email - settings: - host: - from_secret: mail_host - username: - from_secret: mail_username - password: - from_secret: mail_password - from: - from_secret: mail_from - when: - status: [ changed, failure ] - -trigger: - event: - - push - - custom - - promote - - rollback \ No newline at end of file