feat: add docker recipe + environment with default interpolation in config
arcad/emissary/pipeline/head There was a failure building this commit
Details
arcad/emissary/pipeline/head There was a failure building this commit
Details
This commit is contained in:
parent
b2b839cab4
commit
0c9d86b850
|
@ -0,0 +1,14 @@
|
||||||
|
/apps
|
||||||
|
/bin
|
||||||
|
/dist
|
||||||
|
/out
|
||||||
|
/tmp
|
||||||
|
/tools
|
||||||
|
/.emissary-token
|
||||||
|
/.env
|
||||||
|
/agent-key.json
|
||||||
|
/server-key.json
|
||||||
|
/CHANGELOG.md
|
||||||
|
/state.json
|
||||||
|
/*.sqlite*
|
||||||
|
/.mktools
|
58
Dockerfile
58
Dockerfile
|
@ -1,4 +1,12 @@
|
||||||
FROM golang:1.19 AS BUILD
|
FROM alpine as certs
|
||||||
|
RUN apk update && apk add ca-certificates curl openssl bash
|
||||||
|
RUN curl -k https://forge.cadoles.com/Cadoles/Jenkins/raw/branch/master/resources/com/cadoles/common/add-letsencrypt-ca.sh | bash
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# Emissary Server #
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
FROM golang:1.21 AS build-emissary-server
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y make
|
&& apt-get install -y make
|
||||||
|
@ -7,9 +15,9 @@ COPY . /src
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
RUN make GORELEASER_ARGS='build --rm-dist --single-target --snapshot' release
|
RUN make mktools && make GORELEASER_ARGS="build --snapshot --clean --single-target --id emissary-server" goreleaser
|
||||||
|
|
||||||
FROM busybox:latest AS RUNTIME
|
FROM busybox:latest AS emissary-server
|
||||||
|
|
||||||
ARG DUMB_INIT_VERSION=1.2.5
|
ARG DUMB_INIT_VERSION=1.2.5
|
||||||
|
|
||||||
|
@ -19,11 +27,47 @@ RUN mkdir -p /usr/local/bin \
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
|
ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
|
||||||
|
|
||||||
COPY --from=BUILD /src/dist/emissary_linux_amd64_v1 /app
|
COPY --from=build-emissary-server /src/dist/emissary-server_linux_amd64_v1 /app
|
||||||
COPY --from=BUILD /src/tmp/config.yml /etc/emissary/config.yml
|
COPY misc/docker/server.yml /etc/emissary/server.yml
|
||||||
|
COPY --from=certs /etc/ssl/certs /etc/ssl/certs
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
ENTRYPOINT ["/app/emissary"]
|
RUN mkdir -p /data
|
||||||
|
|
||||||
CMD ["server", "run", "-c", "/etc/emissary/config.yml"]
|
CMD [ "/app/emissary", "-c", "/etc/emissary/config.yml", "server", "run"]
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# Emissary Agent #
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
FROM golang:1.21 AS build-emissary-agent
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y make
|
||||||
|
|
||||||
|
COPY . /src
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
RUN make mktools && make GORELEASER_ARGS="build --snapshot --clean --single-target --id emissary-agent" goreleaser
|
||||||
|
|
||||||
|
FROM busybox:latest AS emissary-agent
|
||||||
|
|
||||||
|
ARG DUMB_INIT_VERSION=1.2.5
|
||||||
|
|
||||||
|
RUN mkdir -p /usr/local/bin \
|
||||||
|
&& wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_x86_64 \
|
||||||
|
&& chmod +x /usr/local/bin/dumb-init
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
|
||||||
|
|
||||||
|
COPY --from=build-emissary-agent /src/dist/emissary-agent_linux_amd64_v1 /app
|
||||||
|
|
||||||
|
COPY --chmod=777 misc/docker/docker-agent-wrapper.sh /usr/local/bin/docker-agent-wrapper
|
||||||
|
COPY misc/docker/agent.yml /etc/emissary/agent.yml
|
||||||
|
COPY --from=certs /etc/ssl/certs /etc/ssl/certs
|
||||||
|
|
||||||
|
RUN mkdir -p /data
|
||||||
|
|
||||||
|
CMD [ "/usr/local/bin/docker-agent-wrapper", "/app/emissary", "-c", "/etc/emissary/agent.yml", "agent", "run" ]
|
23
Makefile
23
Makefile
|
@ -1,12 +1,11 @@
|
||||||
LINT_ARGS ?= --timeout 5m
|
LINT_ARGS ?= --timeout 5m
|
||||||
GORELEASER_VERSION ?= v1.13.1
|
GORELEASER_ARGS ?= release --snapshot --rm-dist --help
|
||||||
GORELEASER_ARGS ?= release --snapshot --rm-dist
|
|
||||||
GITCHLOG_ARGS ?=
|
GITCHLOG_ARGS ?=
|
||||||
SHELL := /bin/bash
|
SHELL := /bin/bash
|
||||||
|
|
||||||
EMISSARY_VERSION ?=
|
EMISSARY_VERSION ?=
|
||||||
|
|
||||||
DOCKER_IMAGE_NAME ?= bornholm/emissary
|
DOCKER_IMAGE_NAME ?= reg.cadoles.com/cadoles/emissary
|
||||||
DOCKER_IMAGE_TAG ?= $(MKT_PROJECT_VERSION)
|
DOCKER_IMAGE_TAG ?= $(MKT_PROJECT_VERSION)
|
||||||
|
|
||||||
GOTEST_ARGS ?= -short
|
GOTEST_ARGS ?= -short
|
||||||
|
@ -71,17 +70,23 @@ dump-config: build-emissary
|
||||||
./bin/emissary config dump > tmp/config.yml
|
./bin/emissary config dump > tmp/config.yml
|
||||||
|
|
||||||
.PHONY: goreleaser
|
.PHONY: goreleaser
|
||||||
goreleaser: .mktools
|
goreleaser: .env .mktools
|
||||||
( set -o allexport && source .env && set +o allexport && VERSION=$(GORELEASER_VERSION) curl -sfL https://goreleaser.com/static/run | GORELEASER_CURRENT_TAG="$(MKT_PROJECT_VERSION)" bash /dev/stdin $(GORELEASER_ARGS) )
|
( set -o allexport && source .env && set +o allexport && curl -sfL https://goreleaser.com/static/run | GORELEASER_CURRENT_TAG="$(MKT_PROJECT_VERSION)" bash /dev/stdin $(GORELEASER_ARGS) )
|
||||||
|
|
||||||
install-git-hooks:
|
install-git-hooks:
|
||||||
git config core.hooksPath .githooks
|
git config core.hooksPath .githooks
|
||||||
|
|
||||||
docker-build:
|
docker-build: docker-build-agent docker-build-server
|
||||||
docker build -t $(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) .
|
|
||||||
|
|
||||||
docker-release:
|
docker-build-%:
|
||||||
docker push $(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)
|
docker build --target emissary-$* -t $(DOCKER_IMAGE_NAME)-$*:latest .
|
||||||
|
|
||||||
|
docker-release: docker-release-agent docker-release-server
|
||||||
|
|
||||||
|
docker-release-%:
|
||||||
|
docker tag $(DOCKER_IMAGE_NAME)-$*:latest $(DOCKER_IMAGE_NAME)-$*:$(DOCKER_IMAGE_TAG)
|
||||||
|
docker push $(DOCKER_IMAGE_NAME)-$*:latest
|
||||||
|
docker push $(DOCKER_IMAGE_NAME)-$*:$(DOCKER_IMAGE_TAG)
|
||||||
|
|
||||||
deploy-openwrt-agent:
|
deploy-openwrt-agent:
|
||||||
$(MAKE) GOARCH="arm" GORELEASER_ARGS='build --single-target --snapshot --clean' goreleaser
|
$(MAKE) GOARCH="arm" GORELEASER_ARGS='build --single-target --snapshot --clean' goreleaser
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
- (FR) - [Premiers pas](./tutorials/fr/first-steps.md)
|
- (FR) - [Premiers pas](./tutorials/fr/first-steps.md)
|
||||||
- (FR) - [Déployer un serveur mandataire inverse sur un agent](./tutorials/fr/deploy-reverse-proxy.md)
|
- (FR) - [Déployer un serveur mandataire inverse sur un agent](./tutorials/fr/deploy-reverse-proxy.md)
|
||||||
- (FR) - [Déployer une configuration UCI personnalisée sur un agent](./tutorials/fr/deploy-uci-configuration.md)
|
- (FR) - [Déployer une configuration UCI personnalisée sur un agent](./tutorials/fr/deploy-uci-configuration.md)
|
||||||
|
- (FR) - [Démarrer un agent avec Docker](./tutorials/fr/docker-agent.md)
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Lancer un agent avec Docker
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker run \
|
||||||
|
--rm -it \
|
||||||
|
--network bridge \
|
||||||
|
-v emissary-agent-data:/data \
|
||||||
|
-e EMISSARY_AGENT_SERVER_URL=<server_url> \
|
||||||
|
reg.cadoles.com/cadoles/emissary-agent:latest
|
||||||
|
```
|
|
@ -10,7 +10,41 @@ import (
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reVar = regexp.MustCompile(`^\${(\w+)}$`)
|
var (
|
||||||
|
interpolationRegExp = regexp.MustCompile(`^\${((?P<varName>\w+)|((?P<varNameWithDefault>\w+):-(?P<defaultValue>[^}]+)))}$`)
|
||||||
|
varNameGroupIndex = interpolationRegExp.SubexpIndex("varName")
|
||||||
|
varNameWithDefaultGroupIndex = interpolationRegExp.SubexpIndex("varNameWithDefault")
|
||||||
|
defaultValueGroupIndex = interpolationRegExp.SubexpIndex("defaultValue")
|
||||||
|
)
|
||||||
|
|
||||||
|
func interpolate(str string, getValueFunc func(name string) string) string {
|
||||||
|
for _, match := range interpolationRegExp.FindAllStringSubmatch(str, -1) {
|
||||||
|
varName := match[varNameWithDefaultGroupIndex]
|
||||||
|
if varName == "" {
|
||||||
|
varName = match[varNameGroupIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
if varName == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultValue := ""
|
||||||
|
if defaultValueGroupIndex < len(match) {
|
||||||
|
defaultValue = match[defaultValueGroupIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
str = getValueFunc(varName)
|
||||||
|
if str == "" {
|
||||||
|
str = defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
func interpolateEnv(str string) string {
|
||||||
|
return interpolate(str, os.Getenv)
|
||||||
|
}
|
||||||
|
|
||||||
type InterpolatedString string
|
type InterpolatedString string
|
||||||
|
|
||||||
|
@ -21,11 +55,7 @@ func (is *InterpolatedString) UnmarshalYAML(value *yaml.Node) error {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
|
*is = InterpolatedString(interpolateEnv(str))
|
||||||
*is = InterpolatedString(os.Getenv(match[1]))
|
|
||||||
} else {
|
|
||||||
*is = InterpolatedString(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -39,9 +69,7 @@ func (ii *InterpolatedInt) UnmarshalYAML(value *yaml.Node) error {
|
||||||
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
|
str = interpolateEnv(str)
|
||||||
str = os.Getenv(match[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
intVal, err := strconv.ParseInt(str, 10, 32)
|
intVal, err := strconv.ParseInt(str, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -62,9 +90,7 @@ func (ib *InterpolatedBool) UnmarshalYAML(value *yaml.Node) error {
|
||||||
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
|
str = interpolateEnv(str)
|
||||||
str = os.Getenv(match[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
boolVal, err := strconv.ParseBool(str)
|
boolVal, err := strconv.ParseBool(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -91,9 +117,7 @@ func (im *InterpolatedMap) UnmarshalYAML(value *yaml.Node) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if match := reVar.FindStringSubmatch(strVal); len(match) > 0 {
|
strVal = interpolateEnv(strVal)
|
||||||
strVal = os.Getenv(match[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
data[key] = strVal
|
data[key] = strVal
|
||||||
}
|
}
|
||||||
|
@ -113,9 +137,7 @@ func (iss *InterpolatedStringSlice) UnmarshalYAML(value *yaml.Node) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for index, value := range data {
|
for index, value := range data {
|
||||||
if match := reVar.FindStringSubmatch(value); len(match) > 0 {
|
value = interpolateEnv(value)
|
||||||
value = os.Getenv(match[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
data[index] = value
|
data[index] = value
|
||||||
}
|
}
|
||||||
|
@ -134,9 +156,7 @@ func (id *InterpolatedDuration) UnmarshalYAML(value *yaml.Node) error {
|
||||||
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
|
str = interpolateEnv(str)
|
||||||
str = os.Getenv(match[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
duration, err := time.ParseDuration(str)
|
duration, err := time.ParseDuration(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type interpolateTestCase struct {
|
||||||
|
String string
|
||||||
|
Data map[string]string
|
||||||
|
Expected string
|
||||||
|
}
|
||||||
|
|
||||||
|
var interpolateTestCases = []interpolateTestCase{
|
||||||
|
{
|
||||||
|
String: "${foo}",
|
||||||
|
Data: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
Expected: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
String: "${hello:-world}",
|
||||||
|
Data: map[string]string{},
|
||||||
|
Expected: "world",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
String: "${hello:-}",
|
||||||
|
Data: map[string]string{},
|
||||||
|
Expected: "${hello:-}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
String: "foo",
|
||||||
|
Data: map[string]string{},
|
||||||
|
Expected: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
String: "",
|
||||||
|
Data: map[string]string{},
|
||||||
|
Expected: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInterpolate(t *testing.T) {
|
||||||
|
for idx, tc := range interpolateTestCases {
|
||||||
|
func(idx int, tc interpolateTestCase) {
|
||||||
|
t.Run(fmt.Sprintf("Case_%d", idx), func(t *testing.T) {
|
||||||
|
result := interpolate(tc.String, func(name string) string {
|
||||||
|
value, exists := tc.Data[name]
|
||||||
|
if !exists {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
})
|
||||||
|
|
||||||
|
if e, g := tc.Expected, result; e != g {
|
||||||
|
t.Errorf("result: expected '%v', got '%v'", tc.Expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}(idx, tc)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
logger:
|
||||||
|
level: ${EMISSARY_AGENT_LOGGER_LEVEL:-1}
|
||||||
|
format: ${EMISSARY_AGENT_LOGGER_FORMAT:-human}
|
||||||
|
sentry:
|
||||||
|
dsn: ${EMISSARY_AGENT_SENTRY_DSN}
|
||||||
|
agent:
|
||||||
|
serverUrl: ${EMISSARY_AGENT_SERVER_URL:-http://127.0.0.1:3000}
|
||||||
|
privateKeyPath: ${EMISSARY_AGENT_PRIVATE_KEY_PATH:-/data/agent-key.json}
|
||||||
|
reconciliationInterval: ${EMISSARY_AGENT_RECONCILIATION_INTERVAL:-30}
|
||||||
|
controllers:
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
stateFile: ${EMISSARY_AGENT_CONTROLLERS_PERSISTENCE_STATE_FILE:-/data/state.json}
|
||||||
|
spec:
|
||||||
|
enabled: true
|
||||||
|
proxy:
|
||||||
|
enabled: true
|
||||||
|
uci:
|
||||||
|
enabled: false
|
||||||
|
app:
|
||||||
|
enabled: true
|
||||||
|
dataDir: ${EMISSARY_AGENT_CONTROLLERS_APP_DATA_DIR:-/data/apps/data}
|
||||||
|
downloadDir: ${EMISSARY_AGENT_CONTROLLERS_APP_DOWNLOAD_DIR:-/data/apps/bundles}
|
||||||
|
sysupgrade:
|
||||||
|
enabled: false
|
||||||
|
mdns:
|
||||||
|
enabled: true
|
||||||
|
collectors:
|
||||||
|
- name: uname
|
||||||
|
command: uname
|
||||||
|
args:
|
||||||
|
- -a
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Generate machine id if not exists
|
||||||
|
if [ ! -f /etc/machine-id ]; then
|
||||||
|
cat /proc/sys/kernel/random/uuid > /etc/machine-id
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec $@
|
|
@ -0,0 +1,35 @@
|
||||||
|
logger:
|
||||||
|
level: ${EMISSARY_SERVER_LOGGER_LEVEL:-1}
|
||||||
|
format: ${EMISSARY_SERVER_LOGGER_FORMAT:-human}
|
||||||
|
sentry:
|
||||||
|
dsn: ${EMISSARY_SERVER_SENTRY_DSN}
|
||||||
|
server:
|
||||||
|
http:
|
||||||
|
host: ${EMISSARY_SERVER_HTTP_HOST:-0.0.0.0}
|
||||||
|
port: ${EMISSARY_SERVER_HTTP_HOST:-3000}
|
||||||
|
database:
|
||||||
|
driver: ${EMISSARY_SERVER_DATABASE_DRIVER:-sqlite}
|
||||||
|
dsn: ${EMISSARY_SERVER_DATABASE_DSN:-sqlite:///data/emissary.sqlite?_pragma=foreign_keys(1)&_pragma=busy_timeout=150000&_pragma=journal_mode=WAL}
|
||||||
|
cors:
|
||||||
|
allowedOrigins:
|
||||||
|
- ${EMISSARY_SERVER_CORS_ALLOWED_ORIGINS:-http://localhost:3001}
|
||||||
|
allowCredentials: ${EMISSARY_SERVER_CORS_ALLOW_CREDENTIALS:-true}
|
||||||
|
allowMethods:
|
||||||
|
- POST
|
||||||
|
- GET
|
||||||
|
- PUT
|
||||||
|
- DELETE
|
||||||
|
allowedHeaders:
|
||||||
|
- Origin
|
||||||
|
- Accept
|
||||||
|
- Content-Type
|
||||||
|
- Authorization
|
||||||
|
- Sentry-Trace
|
||||||
|
debug: ${EMISSARY_SERVER_CORS_DEBUG:-false}
|
||||||
|
auth:
|
||||||
|
local:
|
||||||
|
privateKeyPath: ${EMISSARY_SERVER_AUTH_LOCAL_PRIVATE_KEY_PATH:-/data/server-key.json}
|
||||||
|
remote:
|
||||||
|
jwksUrl: "${EMISSARY_SERVER_AUTH_REMOTE_JWKS_URL}"
|
||||||
|
roleExtractionRules:
|
||||||
|
- "${EMISSARY_SERVER_AUTH_ROLE_EXTRACTION_RULES_0:-jwt.role != nil ? str(jwt.role) : ''}"
|
Loading…
Reference in New Issue