mirror of
https://github.com/ipedrazas/drone-helm.git
synced 2026-06-04 18:24:13 +08:00
740191bf1d
Hi, All commits that belong to pull request will have such build event, therefore we will see only help message instead of deployment.
366 lines
8.7 KiB
Go
366 lines
8.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/alecthomas/template"
|
|
)
|
|
|
|
var HELM_BIN = "/bin/helm"
|
|
var KUBECONFIG = "/root/.kube/kubeconfig"
|
|
|
|
type (
|
|
// Config maps the params we need to run Helm
|
|
Config struct {
|
|
APIServer string `json:"api_server"`
|
|
Token string `json:"token"`
|
|
ServiceAccount string `json:"service_account"`
|
|
KubeConfig string `json:"kube_config"`
|
|
HelmCommand string `json:"helm_command"`
|
|
SkipTLSVerify bool `json:"tls_skip_verify"`
|
|
Namespace string `json:"namespace"`
|
|
Release string `json:"release"`
|
|
Chart string `json:"chart"`
|
|
Version string `json:"version"`
|
|
Values string `json:"values"`
|
|
ValuesFiles string `json:"values_files"`
|
|
Debug bool `json:"debug"`
|
|
DryRun bool `json:"dry_run"`
|
|
Secrets []string `json:"secrets"`
|
|
Prefix string `json:"prefix"`
|
|
TillerNs string `json:"tiller_ns"`
|
|
Wait bool `json:"wait"`
|
|
RecreatePods bool `json:"recreate_pods"`
|
|
Upgrade bool `json:"upgrade"`
|
|
CanaryImage bool `json:"canary_image"`
|
|
ClientOnly bool `json:"client_only"`
|
|
ReuseValues bool `json:"reuse_values"`
|
|
Timeout string `json:"timeout"`
|
|
Force bool `json:"force"`
|
|
HelmRepos []string `json:"helm_repos"`
|
|
}
|
|
// Plugin default
|
|
Plugin struct {
|
|
Config Config
|
|
command []string
|
|
}
|
|
)
|
|
|
|
func setHelpCommand(p *Plugin) {
|
|
p.command = []string{""}
|
|
}
|
|
func setDeleteCommand(p *Plugin) {
|
|
delete := make([]string, 2)
|
|
delete[0] = "delete"
|
|
delete[1] = p.Config.Release
|
|
|
|
if p.Config.TillerNs != "" {
|
|
delete = append(delete, "--tiller-namespace")
|
|
delete = append(delete, p.Config.TillerNs)
|
|
}
|
|
if p.Config.DryRun {
|
|
delete = append(delete, "--dry-run")
|
|
}
|
|
|
|
p.command = delete
|
|
}
|
|
|
|
func setUpgradeCommand(p *Plugin) {
|
|
upgrade := make([]string, 2)
|
|
upgrade[0] = "upgrade"
|
|
upgrade[1] = "--install"
|
|
|
|
if p.Config.Release != "" {
|
|
upgrade = append(upgrade, p.Config.Release)
|
|
}
|
|
upgrade = append(upgrade, p.Config.Chart)
|
|
if p.Config.Version != "" {
|
|
upgrade = append(upgrade, "--version")
|
|
upgrade = append(upgrade, p.Config.Version)
|
|
}
|
|
if p.Config.Values != "" {
|
|
upgrade = append(upgrade, "--set")
|
|
upgrade = append(upgrade, unQuote(p.Config.Values))
|
|
}
|
|
if p.Config.ValuesFiles != "" {
|
|
for _, valuesFile := range strings.Split(p.Config.ValuesFiles, ",") {
|
|
upgrade = append(upgrade, "--values")
|
|
upgrade = append(upgrade, valuesFile)
|
|
}
|
|
}
|
|
if p.Config.Namespace != "" {
|
|
upgrade = append(upgrade, "--namespace")
|
|
upgrade = append(upgrade, p.Config.Namespace)
|
|
}
|
|
if p.Config.TillerNs != "" {
|
|
upgrade = append(upgrade, "--tiller-namespace")
|
|
upgrade = append(upgrade, p.Config.TillerNs)
|
|
}
|
|
if p.Config.DryRun {
|
|
upgrade = append(upgrade, "--dry-run")
|
|
}
|
|
if p.Config.Debug {
|
|
upgrade = append(upgrade, "--debug")
|
|
}
|
|
if p.Config.Wait {
|
|
upgrade = append(upgrade, "--wait")
|
|
}
|
|
if p.Config.RecreatePods {
|
|
upgrade = append(upgrade, "--recreate-pods")
|
|
}
|
|
if p.Config.ReuseValues {
|
|
upgrade = append(upgrade, "--reuse-values")
|
|
}
|
|
if p.Config.Timeout != "" {
|
|
upgrade = append(upgrade, "--timeout")
|
|
upgrade = append(upgrade, p.Config.Timeout)
|
|
}
|
|
if p.Config.Force {
|
|
upgrade = append(upgrade, "--force")
|
|
}
|
|
p.command = upgrade
|
|
|
|
}
|
|
|
|
func setHelmCommand(p *Plugin) {
|
|
|
|
switch p.Config.HelmCommand {
|
|
case "upgrade":
|
|
setUpgradeCommand(p)
|
|
case "delete":
|
|
setDeleteCommand(p)
|
|
default:
|
|
switch os.Getenv("DRONE_BUILD_EVENT") {
|
|
case "push", "tag", "deployment", "pull_request":
|
|
setUpgradeCommand(p)
|
|
case "delete":
|
|
setDeleteCommand(p)
|
|
default:
|
|
setHelpCommand(p)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
var repoExp = regexp.MustCompile(`^(?P<name>[\w-]+)=(?P<url>(http|https)://[\w-./:]+)`)
|
|
|
|
// parseRepo returns map of regex capture groups (name, url)
|
|
func parseRepo(repo string) (map[string]string, error) {
|
|
matches := repoExp.FindStringSubmatch(repo)
|
|
if len(matches) < 1 {
|
|
return nil, fmt.Errorf("Invalid repo definition: %s", repo)
|
|
}
|
|
result := make(map[string]string)
|
|
for i, name := range repoExp.SubexpNames() {
|
|
if i != 0 {
|
|
result[name] = matches[i]
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func doHelmRepoAdd(repo string) ([]string, error) {
|
|
repoMap, err := parseRepo(unQuote(repo))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
repoAdd := []string{
|
|
"repo",
|
|
"add",
|
|
repoMap["name"],
|
|
repoMap["url"],
|
|
}
|
|
return repoAdd, nil
|
|
}
|
|
|
|
func doHelmInit(p *Plugin) []string {
|
|
init := make([]string, 1)
|
|
init[0] = "init"
|
|
if p.Config.TillerNs != "" {
|
|
init = append(init, "--tiller-namespace")
|
|
init = append(init, p.Config.TillerNs)
|
|
}
|
|
if p.Config.ClientOnly {
|
|
init = append(init, "--client-only")
|
|
}
|
|
if p.Config.Upgrade {
|
|
init = append(init, "--upgrade")
|
|
}
|
|
if p.Config.CanaryImage {
|
|
init = append(init, "--canary-image")
|
|
}
|
|
|
|
return init
|
|
|
|
}
|
|
|
|
// Exec default method
|
|
func (p *Plugin) Exec() error {
|
|
if p.Config.Debug {
|
|
p.debugEnv()
|
|
}
|
|
|
|
// create /root/.kube/config file if not exists
|
|
if _, err := os.Stat(p.Config.KubeConfig); os.IsNotExist(err) {
|
|
resolveSecrets(p)
|
|
if p.Config.APIServer == "" {
|
|
return fmt.Errorf("Error: API Server is needed to deploy.")
|
|
}
|
|
if p.Config.Token == "" {
|
|
return fmt.Errorf("Error: Token is needed to deploy.")
|
|
}
|
|
|
|
initialiseKubeconfig(&p.Config, KUBECONFIG, p.Config.KubeConfig)
|
|
}
|
|
|
|
if p.Config.Debug {
|
|
p.debug()
|
|
}
|
|
|
|
init := doHelmInit(p)
|
|
err := runCommand(init)
|
|
if err != nil {
|
|
return fmt.Errorf("Error running helm command: " + strings.Join(init[:], " "))
|
|
}
|
|
|
|
if len(p.Config.HelmRepos) > 0 {
|
|
for _, repo := range p.Config.HelmRepos {
|
|
repoAdd, err := doHelmRepoAdd(repo)
|
|
if err == nil {
|
|
if p.Config.Debug {
|
|
log.Println("adding helm repo: " + strings.Join(repoAdd[:], " "))
|
|
}
|
|
|
|
if err = runCommand(repoAdd); err != nil {
|
|
return fmt.Errorf("Error adding helm repo: " + err.Error())
|
|
}
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
setHelmCommand(p)
|
|
|
|
if p.Config.Debug {
|
|
log.Println("helm command: " + strings.Join(p.command, " "))
|
|
}
|
|
|
|
err = runCommand(p.command)
|
|
if err != nil {
|
|
return fmt.Errorf("Error running helm command: " + strings.Join(p.command[:], " "))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func initialiseKubeconfig(params *Config, source string, target string) error {
|
|
f, err := os.Create(target)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
// parse template
|
|
t, _ := template.ParseFiles(source)
|
|
// execute template
|
|
return t.Execute(f, params)
|
|
}
|
|
|
|
func runCommand(params []string) error {
|
|
cmd := new(exec.Cmd)
|
|
cmd = exec.Command(HELM_BIN, params...)
|
|
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
err := cmd.Run()
|
|
return err
|
|
}
|
|
|
|
func resolveSecrets(p *Plugin) {
|
|
p.Config.Values = resolveEnvVar(p.Config.Values, p.Config.Prefix, p.Config.Debug)
|
|
p.Config.APIServer = resolveEnvVar("${API_SERVER}", p.Config.Prefix, p.Config.Debug)
|
|
p.Config.Token = resolveEnvVar("${KUBERNETES_TOKEN}", p.Config.Prefix, p.Config.Debug)
|
|
p.Config.ServiceAccount = resolveEnvVar("${SERVICE_ACCOUNT}", p.Config.Prefix, p.Config.Debug)
|
|
if p.Config.ServiceAccount == "" {
|
|
p.Config.ServiceAccount = "helm"
|
|
}
|
|
}
|
|
|
|
// getEnvVars will return [${TAG} {TAG} TAG]
|
|
func getEnvVars(envvars string) [][]string {
|
|
re := regexp.MustCompile(`\$(\{?(\w+)\}?)\.?`)
|
|
extracted := re.FindAllStringSubmatch(envvars, -1)
|
|
return extracted
|
|
}
|
|
|
|
func resolveEnvVar(key string, prefix string, debug bool) string {
|
|
envvars := getEnvVars(key)
|
|
return replaceEnvvars(envvars, prefix, key, debug)
|
|
}
|
|
|
|
func replaceEnvvars(envvars [][]string, prefix string, s string, debug bool) string {
|
|
for _, envvar := range envvars {
|
|
envvarName := envvar[0]
|
|
envvarKey := envvar[2]
|
|
prefixedKey := strings.ToUpper(prefix + "_" + envvarKey)
|
|
envval := os.Getenv(prefixedKey)
|
|
if debug {
|
|
fmt.Printf("-ReplVar: %s => %s-- %s\n", prefixedKey, envvarKey, envval)
|
|
}
|
|
|
|
if envval == "" {
|
|
envval = os.Getenv(envvarKey)
|
|
}
|
|
|
|
if strings.Contains(s, envvarName) {
|
|
s = strings.Replace(s, envvarName, envval, -1)
|
|
}
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// unQuote removes quotes if present
|
|
func unQuote(s string) string {
|
|
unquoted, err := strconv.Unquote(s)
|
|
if err != nil {
|
|
// ignore error and return original string
|
|
return s
|
|
}
|
|
return unquoted
|
|
}
|
|
|
|
func (p *Plugin) debugEnv() {
|
|
// debug env vars
|
|
for _, e := range os.Environ() {
|
|
fmt.Println("-Var:--", e)
|
|
}
|
|
}
|
|
|
|
func (p *Plugin) debug() {
|
|
fmt.Println(p)
|
|
// debug plugin obj
|
|
fmt.Printf("Api server: %s \n", p.Config.APIServer)
|
|
fmt.Printf("Values: %s \n", p.Config.Values)
|
|
fmt.Printf("Secrets: %s \n", p.Config.Secrets)
|
|
fmt.Printf("Helm Repos: %s \n", p.Config.HelmRepos)
|
|
fmt.Printf("ValuesFiles: %s \n", p.Config.ValuesFiles)
|
|
kubeconfig, err := ioutil.ReadFile(KUBECONFIG)
|
|
if err == nil {
|
|
fmt.Println(string(kubeconfig))
|
|
}
|
|
|
|
config, err := ioutil.ReadFile(p.Config.KubeConfig)
|
|
if err == nil {
|
|
fmt.Println(string(config))
|
|
}
|
|
}
|