From 34ec66358ae03c8968e041a0393ac28606796af7 Mon Sep 17 00:00:00 2001 From: viktorbarzin Date: Mon, 15 Mar 2021 23:32:56 +0000 Subject: [PATCH] add cli client for repo --- cli/README.md | 2 + cli/git.go | 50 +++++++++++ cli/go.mod | 10 +++ cli/go.sum | 66 ++++++++++++++ cli/main.go | 94 +++++++++++++++++++ cli/vpn.go | 99 +++++++++++++++++++++ main.tf | 4 + modules/kubernetes/main.tf | 4 + modules/kubernetes/webhook_handler/main.tf | 10 +++ modules/kubernetes/wireguard/main.tf | 2 +- terraform.tfvars | Bin 10670 -> 10784 bytes 11 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 cli/README.md create mode 100644 cli/git.go create mode 100644 cli/go.mod create mode 100644 cli/go.sum create mode 100644 cli/main.go create mode 100644 cli/vpn.go diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 00000000..48b83c93 --- /dev/null +++ b/cli/README.md @@ -0,0 +1,2 @@ +# What is this? +This is a CLI to manipulate files in the terraform repo and commit and push them diff --git a/cli/git.go b/cli/git.go new file mode 100644 index 00000000..eefd721a --- /dev/null +++ b/cli/git.go @@ -0,0 +1,50 @@ +package main + +import ( + "os" + + "github.com/go-git/go-billy/v5" + "github.com/go-git/go-billy/v5/memfs" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/transport/http" + memory "github.com/go-git/go-git/v5/storage/memory" + "github.com/golang/glog" + "github.com/pkg/errors" +) + +const ( + repository = "https://github.com/ViktorBarzin/infra" +) + +var ( + gitUser = os.Getenv("GIT_USER") + gitToken = os.Getenv("GIT_TOKEN") +) + +type GitFS struct { + repo *git.Repository + fs billy.Filesystem + auth *http.BasicAuth +} + +func NewGitFS(repoURL string) (*GitFS, error) { + glog.Infof("initializing new git fs from repo url: %s", repoURL) + auth := &http.BasicAuth{ + Username: gitUser, + Password: gitToken, + } + storer := memory.NewStorage() + + r, err := git.Clone(storer, g.fs, &git.CloneOptions{ + URL: repository, + Auth: auth, + }) + if err != nil { + return nil, errors.Wrapf(err, "failed to clone repo from repo url '%s'", repoURL) + } + return &GitFS{repo: r, fs: memfs.New(), auth: auth}, nil +} + +func (g *GitFS) Push() error { + return g.repo.Push(&git.PushOptions{Auth: g.auth}) +} diff --git a/cli/go.mod b/cli/go.mod new file mode 100644 index 00000000..db7194c2 --- /dev/null +++ b/cli/go.mod @@ -0,0 +1,10 @@ +module viktorbarzin/infra/cli + +go 1.16 + +require ( + github.com/go-git/go-billy/v5 v5.0.0 // indirect + github.com/go-git/go-git/v5 v5.2.0 // indirect + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect + github.com/pkg/errors v0.9.1 // indirect +) diff --git a/cli/go.sum b/cli/go.sum new file mode 100644 index 00000000..e9884f85 --- /dev/null +++ b/cli/go.sum @@ -0,0 +1,66 @@ +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI= +github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= +github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/cli/main.go b/cli/main.go new file mode 100644 index 00000000..cc853315 --- /dev/null +++ b/cli/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "flag" + "fmt" + + "github.com/go-git/go-git/v5" + "github.com/golang/glog" + "github.com/pkg/errors" +) + +const ( + useCaseFlagName = "use-case" + repoRootFlagName = "repo-root" +) + +var ( + validUseCases = []string{"setup-vpn"} +) + +func main() { + err := run() + if err != nil { + glog.Fatalf("run failed: %s", err.Error()) + } +} + +func run() error { + useCase := flag.String(useCaseFlagName, "", fmt.Sprintf("Use case to run. Available use cases are: %+v", validUseCases)) + // repoRootParam := flag.String(repoRootFlagName, "", fmt.Sprintf("Path to the root of the infra repository.")) + + // VPN flags + vpnClientName := flag.String(vpnClientNameFlagName, "", fmt.Sprintf("Friendly VPN user name.")) + vpnClientPubKey := flag.String(vpnClientPubKeyFlagName, "", fmt.Sprintf("VPN client public key.")) + + flag.Set("logtostderr", "true") + flag.Set("stderrthreshold", "WARNING") + flag.Set("v", "2") + flag.Parse() + + // if *repoRootParam == "" { + // return fmt.Errorf("'-%s' flag must not be empty", repoRootFlagName) + // } + if *useCase == "" { + return fmt.Errorf("'-%s' flag must not be empty", useCaseFlagName) + } + // repoRoot, err := filepath.Abs(*repoRootParam) + // if err != nil { + // return errors.Wrapf(err, "failed to create absolute path from %s", repoRoot) + // } + + glog.Infof("Use case is: %s", *useCase) + // glog.Infof("Repo root is: %s", repoRoot) + + gitFs, err := NewGitFS(repository) + if err != nil { + return errors.Wrapf(err, "failed to initialize git fs") + } + worktree, err := gitFs.repo.Worktree() + if err != nil { + return errors.Wrapf(err, "failed to get worktree") + } + + switch *useCase { + case vpnUseCaseFlagName: + // get last used ip and increment + lastIP, err := getAndUpdateIP(gitFs, vpnLastIPConfFileRelative) + if err != nil { + return errors.Wrapf(err, "failed to get valid last ip from file %s", vpnLastIPConfFileRelative) + } + // insert new vpn client config + err = addVPNClient(gitFs, *vpnClientName, *vpnClientPubKey, vpnClientsConfFileRelative, lastIP) + if err != nil { + return errors.Wrapf(err, "failed to add vpn client") + } + // commit changes + if _, err = worktree.Commit("Added new VPN client config (still testing) [CI SKIP]", &git.CommitOptions{All: true}); err != nil { + return errors.Wrapf(err, "failed to commit") + } + default: + err = errors.New(fmt.Sprintf("unsupported use case: %s", *useCase)) + } + if err != nil { + return err + } + if err = gitFs.Push(); err != nil { + return errors.Wrapf(err, "failed to push changes") + } + + // err = gitRepo() + // glog.Infof(err.Error()) + + return nil +} diff --git a/cli/vpn.go b/cli/vpn.go new file mode 100644 index 00000000..28b510a9 --- /dev/null +++ b/cli/vpn.go @@ -0,0 +1,99 @@ +package main + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "strings" + + "github.com/golang/glog" + "github.com/pkg/errors" +) + +const ( + vpnUseCaseFlagName = "vpn" + vpnClientNameFlagName = "vpn-client-name" + vpnClientPubKeyFlagName = "vpn-pub-key" + vpnClientsConfFileRelative = "modules/kubernetes/wireguard/extra/clients.conf" + vpnLastIPConfFileRelative = "modules/kubernetes/wireguard/extra/last_ip.txt" +) + +// addVPNClient inserts new client config +func addVPNClient(gitFs *GitFS, clientName, publicKey, clientsConfPath, ip string) error { + if clientName == "" { + return fmt.Errorf("client name must not be empty when creating a new vpn config") + } + if publicKey == "" { + return fmt.Errorf("public key cannot be empty when creating new vpn config") + } + contents := "[Peer]\n# friendly_name = " + clientName + "\nPublicKey = " + publicKey + "\nAllowedIPs = " + ip + "\n\n" + glog.Infof("adding the following config: \n%s", contents) + f, err := gitFs.fs.OpenFile(clientsConfPath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + return errors.Wrapf(err, "failed to open client configs file to add new vpn client") + } + defer f.Close() + + if _, err = f.Write([]byte(contents)); err != nil { + return errors.Wrapf(err, "failed to write config to file") + } + + glog.Infof("successfully added new vpn client config for %s", clientName) + return nil +} + +func incrementIP(origIP, cidr string) (string, error) { + ip := net.ParseIP(origIP) + _, ipNet, err := net.ParseCIDR(cidr) + if err != nil { + return origIP, err + } + for i := len(ip) - 1; i >= 0; i-- { + ip[i]++ + if ip[i] != 0 { + break + } + } + if !ipNet.Contains(ip) { + return origIP, errors.New("overflowed CIDR while incrementing IP") + } + return ip.String(), nil +} + +// getAndUpdateIP Reads `fileName`, tries to get the ip, increments it, tries to write it back and returns the new address +func getAndUpdateIP(gitFs *GitFS, fileName string) (string, error) { + bytes, err := ioutil.ReadFile(fileName) + if err != nil { + return "", errors.Wrapf(err, "filed to read file %s", fileName) + } + errPrefix := "file has incorrect format: " + content := strings.TrimSpace(string(bytes)) + lines := strings.Split(content, "\n") + if len(lines) != 1 { + return "", fmt.Errorf(errPrefix + fmt.Sprintf("expected 1 line got %d", len(lines))) + } + lineSplit := strings.Split(lines[0], " ") + if len(lineSplit) < 1 { + return "", fmt.Errorf("expected non empty line") + } + ipcidr := strings.Split(lineSplit[len(lineSplit)-1], "/") + ipAddr := ipcidr[0] + cidr := ipcidr[1] + incrementedIP, err := incrementIP(ipAddr, strings.Join(ipcidr, "/")) + if err != nil { + return "", errors.Wrapf(err, "failed to increment ip for string '%s'", ipcidr) + } + + // Write back updated ip + fileContents := fmt.Sprintf("# DO NOT MANUALLY EDIT THIS LINE. Last IP: %s", incrementedIP+"/"+cidr) + f, err := gitFs.fs.OpenFile(fileName, os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + return "", errors.Wrapf(err, "failed to open file %s for writing", fileName) + } + if _, err = f.Write([]byte(fileContents)); err != nil { + return "", errors.Wrapf(err, "failed to write back new ip to file %s contents %s", fileName, fileContents) + } + glog.Infof("new ip: %s", incrementedIP) + return incrementedIP + "/32", nil +} diff --git a/main.tf b/main.tf index de29f032..897cad09 100644 --- a/main.tf +++ b/main.tf @@ -35,6 +35,8 @@ variable "oauth_client_secret" {} variable "webhook_handler_fb_verify_token" {} variable "webhook_handler_fb_page_token" {} variable "webhook_handler_fb_app_secret" {} +variable "webhook_handler_git_user" {} +variable "webhook_handler_git_token" {} variable "ansible_prefix" { default = "ANSIBLE_VAULT_PASSWORD_FILE=~/.ansible/vault_pass.txt ansible-playbook -i playbook/hosts.yaml playbook/linux.yml -t linux/initial_setup" @@ -187,6 +189,8 @@ module "kubernetes_cluster" { webhook_handler_fb_verify_token = var.webhook_handler_fb_verify_token webhook_handler_fb_page_token = var.webhook_handler_fb_page_token webhook_handler_fb_app_secret = var.webhook_handler_fb_app_secret + webhook_handler_git_user = var.webhook_handler_git_user + webhook_handler_git_token = var.webhook_handler_git_token wireguard_wg_0_conf = var.wireguard_wg_0_conf wireguard_wg_0_key = var.wireguard_wg_0_key diff --git a/modules/kubernetes/main.tf b/modules/kubernetes/main.tf index 89d29aea..01f80774 100644 --- a/modules/kubernetes/main.tf +++ b/modules/kubernetes/main.tf @@ -22,6 +22,8 @@ variable "oauth_client_secret" {} variable "webhook_handler_fb_verify_token" {} variable "webhook_handler_fb_page_token" {} variable "webhook_handler_fb_app_secret" {} +variable "webhook_handler_git_user" {} +variable "webhook_handler_git_token" {} resource "null_resource" "core_services" { # List all the core modules that must be provisioned first @@ -162,6 +164,8 @@ module "webhook_handler" { fb_verify_token = var.webhook_handler_fb_verify_token fb_page_token = var.webhook_handler_fb_page_token fb_app_secret = var.webhook_handler_fb_app_secret + git_user = var.webhook_handler_git_user + git_token = var.webhook_handler_git_token depends_on = [null_resource.core_services] } diff --git a/modules/kubernetes/webhook_handler/main.tf b/modules/kubernetes/webhook_handler/main.tf index d26a4e41..e82ca68c 100644 --- a/modules/kubernetes/webhook_handler/main.tf +++ b/modules/kubernetes/webhook_handler/main.tf @@ -4,6 +4,8 @@ variable "webhook_secret" {} variable "fb_verify_token" {} variable "fb_page_token" {} variable "fb_app_secret" {} +variable "git_user" {} +variable "git_token" {} resource "kubernetes_namespace" "webhook-handler" { metadata { @@ -105,6 +107,14 @@ resource "kubernetes_deployment" "webhook_handler" { name = "CONFIG" value = "./chatbot/config/viktorwebservices.yaml" } + env { + name = "GIT_USER" + value = var.git_user + } + env { + name = "GIT_TOKEN" + value = var.git_token + } } } } diff --git a/modules/kubernetes/wireguard/main.tf b/modules/kubernetes/wireguard/main.tf index c9fe5488..cced1f0f 100644 --- a/modules/kubernetes/wireguard/main.tf +++ b/modules/kubernetes/wireguard/main.tf @@ -26,7 +26,7 @@ resource "kubernetes_config_map" "wg_0_conf" { data = { "setup-firewall.sh" = var.firewall_sh - "wg0.conf" = var.wg_0_conf + "wg0.conf" = format("%s%s", var.wg_0_conf, file("${path.module}/extra/clients.conf")) } } diff --git a/terraform.tfvars b/terraform.tfvars index 7b7430e4cc547d15b1acc29dac7b77d18a57f1ea..28683daab32c72c36777442058411ebc97c8e40e 100644 GIT binary patch literal 10784 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 literal 10670 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