Add first version of the deployment manager for testing

This commit is contained in:
Marco Streich 2021-01-11 11:03:19 +01:00
commit cc9fb0410c
14 changed files with 909 additions and 0 deletions

View File

@ -0,0 +1,3 @@
*.sw*
Makefile
README.md

29
deploymentagent/Makefile Normal file
View 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
View 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.

View 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"

View 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"]

View 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
}

View 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")
}
})
}

View 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
)

View 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=

View 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)
}
}

View 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
}

View 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)
}
}

View 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"]

View 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"