Add first version of the deployment manager for testing
This commit is contained in:
commit
cc9fb0410c
3
deploymentagent/.dockerignore
Normal file
3
deploymentagent/.dockerignore
Normal file
@ -0,0 +1,3 @@
|
||||
*.sw*
|
||||
Makefile
|
||||
README.md
|
29
deploymentagent/Makefile
Normal file
29
deploymentagent/Makefile
Normal file
@ -0,0 +1,29 @@
|
||||
IMAGE=deploymentagent
|
||||
VERSION=latest
|
||||
KEY_NAME=/path/to/private-key
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
.PHONY: build run clean test
|
||||
|
||||
all: build
|
||||
|
||||
build: ## Build the container
|
||||
export SSH_KEY="$$(base64 $(KEY_NAME))" && \
|
||||
export VERSION=$(VERSION) && \
|
||||
export IMAGE=$(IMAGE) && \
|
||||
docker-compose build
|
||||
|
||||
run: ## Launch testing environment
|
||||
export SSH_KEY= && \
|
||||
export VERSION=$(VERSION) && \
|
||||
export IMAGE=$(IMAGE) && \
|
||||
docker-compose up
|
||||
|
||||
clean: ## Remove containers and volumes
|
||||
docker-compose down --remove-orphans && docker volume rm podman-socket
|
||||
|
||||
test: ## Run unit tests (be sure to build and launch the testing environment first)
|
||||
cd src/deploymentagent; go test -v
|
||||
|
||||
help:
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
161
deploymentagent/README.md
Normal file
161
deploymentagent/README.md
Normal file
@ -0,0 +1,161 @@
|
||||
# Deployment API
|
||||
|
||||
```
|
||||
.
|
||||
|-- Makefile
|
||||
|-- README.md
|
||||
|-- docker-compose.yml
|
||||
`-- src
|
||||
|-- deploymentagent # Code and tests for the deployment agent
|
||||
`-- podman # Podman inside of Docker replicating behavior on the live server for local testing
|
||||
```
|
||||
|
||||
Accepts a deployment specification to produce a deployment state file.
|
||||
|
||||
## Setup
|
||||
|
||||
This project makes use of `gitlab.com/infektcommon/settings`, in order to be able to build it locally with Docker, you will need to provide the *path* to an ssh private key with access to Gitlab in the `Makefile`, like so:
|
||||
|
||||
```Makefile
|
||||
$ cat Makefile
|
||||
IMAGE=deploymentagent
|
||||
VERSION=latest
|
||||
KEY_NAME=/home/user/.ssh/my-key
|
||||
```
|
||||
|
||||
## Testing
|
||||
### Launch the testing environment
|
||||
```sh
|
||||
make build
|
||||
make run
|
||||
```
|
||||
### Launch unit tests in a separate shell
|
||||
```sh
|
||||
make test
|
||||
```
|
||||
### Cleanup after you are done
|
||||
Removes dangling containers and volumes
|
||||
```sh
|
||||
make clean
|
||||
```
|
||||
|
||||
## Data formats
|
||||
|
||||
### Deployment specification
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"app" : "foo",
|
||||
"version" : "latest"
|
||||
},
|
||||
{
|
||||
"app" : "bar",
|
||||
"version" : "latest"
|
||||
}
|
||||
]
|
||||
```
|
||||
If the same app is specified multiple times, the last entry in the list take precedence.
|
||||
|
||||
### Deployment state
|
||||
|
||||
```json
|
||||
{
|
||||
"foo": "latest",
|
||||
"bar": "latest",
|
||||
}
|
||||
```
|
||||
The deployment state file is intended to be read by Nix, which will produce a systemd unit file that configures the Podman container.
|
||||
|
||||
## Commands
|
||||
|
||||
```sh
|
||||
curl -u'testuser:testpass' -i -XPOST localhost:8080/deploy -d '[{"app":"alpine", "version": "3.12"}]'
|
||||
{"Message": "Queued for deployment", "DeploymentId": "1609855921147639"}
|
||||
```
|
||||
|
||||
```sh
|
||||
curl -u'testuser:testpass' -XGET localhost:8080/status | json_pp
|
||||
```
|
||||
```json
|
||||
[
|
||||
{
|
||||
"Id": 1609855921147639,
|
||||
"Status": "queued",
|
||||
"StatusMessage": "Queued for deployment",
|
||||
"DeploymentSpec": [
|
||||
{
|
||||
"app": "alpine",
|
||||
"version": "3.12"
|
||||
}
|
||||
],
|
||||
"DeploymentState": null
|
||||
}
|
||||
]
|
||||
```
|
||||
.. deployment in progress ..
|
||||
```sh
|
||||
curl -u'testuser:testpass' -XGET localhost:8080/status | json_pp
|
||||
```
|
||||
```json
|
||||
[
|
||||
{
|
||||
"Id": 1610356267147639,
|
||||
"Status": "deployed",
|
||||
"StatusMessage": "Deployment successful",
|
||||
"DeploymentSpec": [
|
||||
{
|
||||
"app": "alpine",
|
||||
"version": "3.12"
|
||||
}
|
||||
],
|
||||
"DeploymentState": {
|
||||
"alpine": "3.12"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
```sh
|
||||
curl -u'testuser:testpass' -i -XPOST localhost:8080/deploy -d '[{"app":"alpine", "version": "latest"}, {"app":"busybox", "version": "latest"}]'
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"Id": 1610356267147639,
|
||||
"Status": "deployed",
|
||||
"StatusMessage": "Deployment successful",
|
||||
"DeploymentSpec": [
|
||||
{
|
||||
"app": "alpine",
|
||||
"version": "3.12"
|
||||
}
|
||||
],
|
||||
"DeploymentState": {
|
||||
"alpine": "3.12"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 1610356267266631,
|
||||
"Status": "deployed",
|
||||
"StatusMessage": "Deployment successful",
|
||||
"DeploymentSpec": [
|
||||
{
|
||||
"app": "alpine",
|
||||
"version": "latst"
|
||||
},
|
||||
{
|
||||
"app": "alpine",
|
||||
"version": "latest"
|
||||
}
|
||||
],
|
||||
"DeploymentState": {
|
||||
"alpine": "latest",
|
||||
"busybox": "latest"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
To test with `api`, `web`, `filestore`, `html2pdf`, the Podman container would need to be authenticated with the Gitlab registry of the glv5 project.
|
34
deploymentagent/docker-compose.yml
Normal file
34
deploymentagent/docker-compose.yml
Normal file
@ -0,0 +1,34 @@
|
||||
services:
|
||||
deploymentagent:
|
||||
image: ${IMAGE}:${VERSION}
|
||||
build:
|
||||
args:
|
||||
SSH_KEY: ${SSH_KEY}
|
||||
context: ./src/deploymentagent
|
||||
environment:
|
||||
- BASIC_AUTH_USERNAME=testuser
|
||||
- BASIC_AUTH_PASSWORD=testpass
|
||||
- CONTAINERS_SOCKET=/tmp/podman/podman.sock
|
||||
ports:
|
||||
- 8080:5000
|
||||
volumes:
|
||||
- podman-socket:/tmp/podman
|
||||
depends_on:
|
||||
- podman
|
||||
podman:
|
||||
image: podman:test
|
||||
privileged: true
|
||||
build:
|
||||
context: ./src/podman
|
||||
environment:
|
||||
- CONTAINERS_SOCKET=/tmp/podman/podman.sock
|
||||
- PORT=8090
|
||||
ports:
|
||||
- 8090:8090
|
||||
volumes:
|
||||
- podman-socket:/tmp/podman
|
||||
|
||||
volumes:
|
||||
podman-socket:
|
||||
|
||||
version: "3.4"
|
23
deploymentagent/src/deploymentagent/Dockerfile
Normal file
23
deploymentagent/src/deploymentagent/Dockerfile
Normal file
@ -0,0 +1,23 @@
|
||||
FROM golang:1.15-alpine3.12 as build
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN apk add --no-cache openssh git && \
|
||||
git config --global url."git@gitlab.com:".insteadOf "https://gitlab.com/" && \
|
||||
mkdir /root/.ssh && \
|
||||
ssh-keyscan gitlab.com >> /root/.ssh/known_hosts
|
||||
|
||||
ARG SSH_KEY
|
||||
ENV SSH_KEY=${SSH_KEY}
|
||||
|
||||
RUN printf "$SSH_KEY" | base64 -d > /root/.ssh/id_rsa && chmod 600 /root/.ssh/id_rsa && \
|
||||
go build -o deploymentagent && \
|
||||
rm /root/.ssh/id_rsa
|
||||
|
||||
FROM alpine:3.12
|
||||
|
||||
COPY --from=build /go/src/app/deploymentagent /usr/local/bin/deploymentagent
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/deploymentagent"]
|
60
deploymentagent/src/deploymentagent/containers.go
Normal file
60
deploymentagent/src/deploymentagent/containers.go
Normal file
@ -0,0 +1,60 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"gitlab.com/infektcommon/settings"
|
||||
)
|
||||
|
||||
var (
|
||||
isGuidelinesApp = map[string]bool{"api": true, "web": true, "filestore": true, "html2pdf": true}
|
||||
guidelinesRegistry = "registry.gitlab.com/infektweb/glv5/"
|
||||
containersSocket string
|
||||
socketHttpTransport = &http.Transport{
|
||||
Dial: func(n, a string) (conn net.Conn, err error) { return net.Dial("unix", containersSocket) },
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
containersSocket = settings.GetSetting("CONTAINERS_SOCKET", "/tmp/podman/podman.sock")
|
||||
}
|
||||
|
||||
type podmanClient struct {
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func (pc *podmanClient) pullImages(deploymentSpec DeploymentSpec, httpClient *http.Client, api string) error {
|
||||
for _, spec := range deploymentSpec {
|
||||
app := spec.App
|
||||
version := spec.Version
|
||||
|
||||
if version == "" {
|
||||
version = "latest"
|
||||
}
|
||||
|
||||
if isGuidelinesApp[app] {
|
||||
app = guidelinesRegistry + app
|
||||
}
|
||||
|
||||
pc.httpClient = httpClient
|
||||
|
||||
if response, err := pc.httpClient.Post(fmt.Sprintf("%s/images/pull?reference=%s:%s", api, app, version), jsonContentType, nil); err == nil {
|
||||
responseBody, _ := ioutil.ReadAll(response.Body)
|
||||
//ioutil.ReadAll(response.Body)
|
||||
fmt.Println(string(responseBody))
|
||||
if response, err := pc.httpClient.Get(fmt.Sprintf("%s/images/%s:%s/exists", api, app, version)); err != nil {
|
||||
return err
|
||||
} else if response.StatusCode != 204 {
|
||||
return errors.New(fmt.Sprintf("The Podman API has not returned an error, but the image \"%s:%s\" could not be found on this server. Likely the image could not be pulled from remote because it does not exist. It's best to check the logs of the deployment agent.", app, version))
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
33
deploymentagent/src/deploymentagent/containers_test.go
Normal file
33
deploymentagent/src/deploymentagent/containers_test.go
Normal file
@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestContainerPullImages(t *testing.T) {
|
||||
testDeploymentSpecs := []DeploymentSpec{
|
||||
{ // multiple existing images
|
||||
{App: "busybox", Version: "latest"},
|
||||
{App: "alpine", Version: "3.12"},
|
||||
},
|
||||
{ // non existing image
|
||||
{App: "busybox", Version: "unknown"},
|
||||
},
|
||||
}
|
||||
|
||||
client := &podmanClient{}
|
||||
|
||||
t.Run("pulling multiple images", func(t *testing.T) {
|
||||
if err := client.pullImages(testDeploymentSpecs[0], &http.Client{}, "http://localhost:8090/v1.0.0/libpod"); err != nil {
|
||||
t.Errorf(fmt.Sprintf("%s", err))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("pulling non existing images", func(t *testing.T) {
|
||||
if err := client.pullImages(testDeploymentSpecs[1], &http.Client{}, "http://localhost:8090/v1.0.0/libpod"); err == nil {
|
||||
t.Errorf("Expected to get an error")
|
||||
}
|
||||
})
|
||||
}
|
28
deploymentagent/src/deploymentagent/go.mod
Normal file
28
deploymentagent/src/deploymentagent/go.mod
Normal file
@ -0,0 +1,28 @@
|
||||
module deploymentagent
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.10.0 // indirect
|
||||
github.com/frankban/quicktest v1.11.3 // indirect
|
||||
github.com/golang/snappy v0.0.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-hclog v0.15.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.0 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.0 // indirect
|
||||
github.com/pierrec/lz4 v2.6.0+incompatible // indirect
|
||||
github.com/stretchr/testify v1.6.1 // indirect
|
||||
gitlab.com/infektcommon/settings v0.0.0-20210104082753-8cdac1bf58b6
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 // indirect
|
||||
golang.org/x/text v0.3.4 // indirect
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
||||
)
|
205
deploymentagent/src/deploymentagent/go.sum
Normal file
205
deploymentagent/src/deploymentagent/go.sum
Normal file
@ -0,0 +1,205 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.15.0 h1:qMuK0wxsoW4D0ddCCYwPSTm4KQv1X1ke3WmPWZ0Mvsk=
|
||||
github.com/hashicorp/go-hclog v0.15.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU=
|
||||
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
|
||||
github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0sMLy8=
|
||||
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
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/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
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/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
|
||||
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDmguYK6iH1A=
|
||||
github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gitlab.com/infektcommon/settings v0.0.0-20210104082753-8cdac1bf58b6 h1:s1eS+fZ5ZngKZyw1oQ5Ubb4CnavabQXTVMlFURJoQWs=
|
||||
gitlab.com/infektcommon/settings v0.0.0-20210104082753-8cdac1bf58b6/go.mod h1:vwzpYzrKQTrUbX7bEssArGT7U1jwZUqQxnvnCJArso0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 h1:joucsQqXmyBVxViHCPFjG3hx8JzIFSaym3l3MM/Jsdg=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU=
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
24
deploymentagent/src/deploymentagent/main.go
Normal file
24
deploymentagent/src/deploymentagent/main.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
inputQueue *DeploymentQueue // queued
|
||||
processQueue *DeploymentQueue // deploying, failed, deployed
|
||||
)
|
||||
|
||||
func main() {
|
||||
server := newDeploymentAgentServer()
|
||||
|
||||
inputQueue = new(DeploymentQueue)
|
||||
processQueue = new(DeploymentQueue)
|
||||
|
||||
go processDeploymentSpecs(&podmanClient{}, &http.Client{Transport: socketHttpTransport}, "http://podman/v1.0.0/libpod")
|
||||
|
||||
if err := http.ListenAndServe(":5000", server); err != nil {
|
||||
log.Fatalf("could not listen on port 5000 %v", err)
|
||||
}
|
||||
}
|
232
deploymentagent/src/deploymentagent/server.go
Normal file
232
deploymentagent/src/deploymentagent/server.go
Normal file
@ -0,0 +1,232 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"gitlab.com/infektcommon/settings"
|
||||
)
|
||||
|
||||
const (
|
||||
jsonContentType = "application/json"
|
||||
)
|
||||
|
||||
var (
|
||||
basicAuthUsername string
|
||||
basicAuthPassword string
|
||||
deploymentStateFile string
|
||||
)
|
||||
|
||||
func init() {
|
||||
basicAuthUsername = settings.GetSetting("BASIC_AUTH_USERNAME", "default")
|
||||
basicAuthPassword = settings.GetSetting("BASIC_AUTH_PASSWORD", "default")
|
||||
deploymentStateFile = settings.GetSetting("DEPLOYMENT_STATE_FILE", "./guidelines.json")
|
||||
}
|
||||
|
||||
type DeploymentAgentServer struct {
|
||||
http.Handler
|
||||
}
|
||||
|
||||
type DeploymentSpec []struct {
|
||||
App string `json:"app"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type QueuedDeploymentSpec struct {
|
||||
Id int64
|
||||
Status string // one of: queued (input queue), deploying, failed, deployed (process queue)
|
||||
StatusMessage string // status or error message
|
||||
DeploymentSpec DeploymentSpec
|
||||
DeploymentState map[string]string
|
||||
}
|
||||
|
||||
type DeploymentQueue struct {
|
||||
items []QueuedDeploymentSpec
|
||||
}
|
||||
|
||||
func (q *DeploymentQueue) Enqueue(id int64, status, statusMessage string, deploymentSpec DeploymentSpec) {
|
||||
q.items = append(q.items, QueuedDeploymentSpec{
|
||||
Id: id,
|
||||
Status: status,
|
||||
StatusMessage: statusMessage,
|
||||
DeploymentSpec: deploymentSpec,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (q *DeploymentQueue) Dequeue() QueuedDeploymentSpec {
|
||||
item := q.items[0]
|
||||
q.items = q.items[1:len(q.items)]
|
||||
return item
|
||||
}
|
||||
|
||||
func (q *DeploymentQueue) GetAll() []QueuedDeploymentSpec {
|
||||
return q.items
|
||||
}
|
||||
|
||||
func (q *DeploymentQueue) updateStatus(id int64, status, statusMessage string, deploymentState map[string]string) {
|
||||
for i := range q.items {
|
||||
if q.items[i].Id == id {
|
||||
p := &q.items[i]
|
||||
p.Status = status
|
||||
p.StatusMessage = statusMessage
|
||||
p.DeploymentState = getDeploymentState()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *DeploymentQueue) String() string {
|
||||
return fmt.Sprint(q.items)
|
||||
}
|
||||
|
||||
func newDeploymentAgentServer() *DeploymentAgentServer {
|
||||
server := new(DeploymentAgentServer)
|
||||
router := http.NewServeMux()
|
||||
|
||||
router.Handle("/deploy", http.HandlerFunc(server.deploymentHandler))
|
||||
router.Handle("/status", http.HandlerFunc(server.statusHandler))
|
||||
|
||||
server.Handler = router
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
func isAuthenticated(w http.ResponseWriter, r http.Request) (http.ResponseWriter, bool) {
|
||||
user, pass, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Header().Add("WWW-Authenticate", `Basic realm=""`)
|
||||
w.Write([]byte(`{"Error": "No credentials present"}`))
|
||||
return w, false
|
||||
}
|
||||
|
||||
if (user != basicAuthUsername && user != "default") || (pass != basicAuthPassword && pass != "default") {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Header().Add("WWW-Authenticate", `Basic realm=""`)
|
||||
w.Write([]byte(`{"Error": "Invalid credentials"}`))
|
||||
return w, false
|
||||
}
|
||||
|
||||
return w, true
|
||||
}
|
||||
|
||||
func (s *DeploymentAgentServer) deploymentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", jsonContentType)
|
||||
if authenticatedResponse, ok := isAuthenticated(w, *r); ok {
|
||||
w = authenticatedResponse
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodPost:
|
||||
queueDeploymentSpec(w, r)
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
w.Write([]byte(`{"Error": "Method not allowed"}`))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DeploymentAgentServer) statusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", jsonContentType)
|
||||
if authenticatedResponse, ok := isAuthenticated(w, *r); ok {
|
||||
w = authenticatedResponse
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
var status []QueuedDeploymentSpec
|
||||
status = append(status, inputQueue.GetAll()...)
|
||||
status = append(status, processQueue.GetAll()...)
|
||||
sort.Slice(status, func(i, j int) bool {
|
||||
return status[i].Id < status[j].Id
|
||||
})
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
json.NewEncoder(w).Encode(status)
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
w.Write([]byte(`{"Error": "Method not allowed"}`))
|
||||
}
|
||||
}
|
||||
|
||||
func queueDeploymentSpec(w http.ResponseWriter, r *http.Request) {
|
||||
var error struct{ Error string }
|
||||
var deploymentSpec DeploymentSpec
|
||||
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
err := json.Unmarshal(body, &deploymentSpec)
|
||||
if err != nil {
|
||||
error.Error = err.Error()
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
json.NewEncoder(w).Encode(&error)
|
||||
} else {
|
||||
message := "Queued for deployment"
|
||||
deploymentId := time.Now().UnixNano() / 1000
|
||||
inputQueue.Enqueue(deploymentId, "queued", message, deploymentSpec)
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
w.Write([]byte(fmt.Sprintf(`{"Message": %q, "DeploymentId": "%d"}`, message, deploymentId)))
|
||||
}
|
||||
}
|
||||
|
||||
func processDeploymentSpecs(pc *podmanClient, hc *http.Client, url string) {
|
||||
fmt.Println("Server ready to schedule deployments")
|
||||
for {
|
||||
time.Sleep(5 * time.Second)
|
||||
if len(inputQueue.GetAll()) > 0 {
|
||||
dequequedDeploymentSpec := inputQueue.Dequeue()
|
||||
processQueue.Enqueue(dequequedDeploymentSpec.Id, "deploying", "Deployment in progress", dequequedDeploymentSpec.DeploymentSpec)
|
||||
err := pc.pullImages(dequequedDeploymentSpec.DeploymentSpec, hc, url)
|
||||
if err != nil {
|
||||
processQueue.updateStatus(dequequedDeploymentSpec.Id, "failed", err.Error(), nil)
|
||||
} else {
|
||||
err := mergeDeploymentState(dequequedDeploymentSpec.DeploymentSpec)
|
||||
if err != nil {
|
||||
processQueue.updateStatus(dequequedDeploymentSpec.Id, "failed", err.Error(), getDeploymentState())
|
||||
} else {
|
||||
processQueue.updateStatus(dequequedDeploymentSpec.Id, "deployed", "Deployment successful", getDeploymentState())
|
||||
}
|
||||
}
|
||||
fmt.Println(processQueue.GetAll())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getDeploymentState() map[string]string {
|
||||
state := make(map[string]string)
|
||||
|
||||
file, _ := ioutil.ReadFile(deploymentStateFile)
|
||||
err := json.Unmarshal([]byte(file), &state)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
func mergeDeploymentState(deploymentSpec DeploymentSpec) error {
|
||||
var deployedApps = []string{}
|
||||
deploymentState := getDeploymentState()
|
||||
|
||||
for k := range deploymentState {
|
||||
deployedApps = append(deployedApps, k)
|
||||
}
|
||||
|
||||
for _, app := range deploymentSpec {
|
||||
deploymentState[app.App] = app.Version
|
||||
}
|
||||
|
||||
file, _ := json.MarshalIndent(deploymentState, "", " ")
|
||||
err := ioutil.WriteFile(deploymentStateFile, file, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
64
deploymentagent/src/deploymentagent/server_test.go
Normal file
64
deploymentagent/src/deploymentagent/server_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var apiTests = []struct {
|
||||
path string
|
||||
method string
|
||||
username string
|
||||
password string
|
||||
payload string
|
||||
wantResponseCode int
|
||||
}{
|
||||
{"/deploy", "POST", "testuser", "testpass", `[{"app":"busybox", "version": "latest"}]`, 202},
|
||||
{"/deploy", "POST", "testuser", "testpass", `[invalid{"app":"foo", "version": "bar"}]`, 400},
|
||||
{"/deploy", "GET", "testuser", "", "", 401},
|
||||
{"/deploy", "GET", "", "", "", 401},
|
||||
{"/deploy", "GET", "testuser", "testpass", "", 405},
|
||||
}
|
||||
|
||||
func TestDeploymentAgentServer(t *testing.T) {
|
||||
handler := http.HandlerFunc(newDeploymentAgentServer().deploymentHandler)
|
||||
|
||||
inputQueue = new(DeploymentQueue)
|
||||
processQueue = new(DeploymentQueue)
|
||||
|
||||
go processDeploymentSpecs(&podmanClient{}, &http.Client{}, "http://localhost:8090/v1.0.0/libpod/")
|
||||
|
||||
basicAuthUsername = "testuser"
|
||||
basicAuthPassword = "testpass"
|
||||
|
||||
for _, test := range apiTests {
|
||||
t.Run(fmt.Sprintf("%q returns %d on %s with payload %q authenticated as %s:%s",
|
||||
test.path,
|
||||
test.wantResponseCode,
|
||||
test.method,
|
||||
test.payload,
|
||||
test.username,
|
||||
test.password),
|
||||
func(t *testing.T) {
|
||||
payload := []byte(test.payload)
|
||||
request, _ := http.NewRequest(test.method, test.path, bytes.NewBuffer(payload))
|
||||
request.SetBasicAuth(test.username, test.password)
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(response, request)
|
||||
|
||||
assertStatus(t, response.Code, test.wantResponseCode)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertStatus(t *testing.T, got, want int) {
|
||||
t.Helper()
|
||||
if got != want {
|
||||
t.Errorf("got %d, want %d", got, want)
|
||||
}
|
||||
|
||||
}
|
8
deploymentagent/src/podman/Dockerfile
Normal file
8
deploymentagent/src/podman/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM fedora:33
|
||||
|
||||
RUN dnf -y install --enablerepo updates-testing podman e2fsprogs socat
|
||||
RUN dd if=/dev/zero of=/containers-disk iflag=fullblock bs=1M count=300 && mkfs.ext4 /containers-disk
|
||||
|
||||
COPY start.sh /start.sh
|
||||
RUN ["chmod", "+x", "/start.sh"]
|
||||
ENTRYPOINT ["/start.sh"]
|
5
deploymentagent/src/podman/start.sh
Normal file
5
deploymentagent/src/podman/start.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
(mount -o loop /containers-disk /var/lib/containers/ && podman system service -t 0 unix://"$CONTAINERS_SOCKET") &
|
||||
|
||||
socat tcp-listen:"$PORT",fork,reuseaddr unix-connect:"$CONTAINERS_SOCKET"
|
Loading…
Reference in New Issue
Block a user