diff --git a/README.md b/README.md index a97855cb..774b12a0 100644 --- a/README.md +++ b/README.md @@ -93,53 +93,55 @@ Generate files from docker container meta-data Options: -config value - config files with template directives. Config files will be merged if this option is specified multiple times. (default []) + config files with template directives. Config files will be merged if this option is specified multiple times. -endpoint string - docker api endpoint (tcp|unix://..). Default unix:///var/run/docker.sock + docker api endpoint (tcp|unix://..). Default unix:///var/run/docker.sock + -include-stopped + include stopped containers -interval int - notify command interval (secs) + notify command interval (secs) -keep-blank-lines - keep blank lines in the output file + keep blank lines in the output file -notify restart xyz - run command after template is regenerated (e.g restart xyz) + run command after template is regenerated (e.g restart xyz) + -notify-container string + container to send a signal to -notify-output - log the output(stdout/stderr) of notify command - -notify-container container-ID - container to send a signal to - -notify-signal signal - signal to send to the -notify-container. -1 to call docker restart. Defaults to 1 aka. HUP. - All available signals available on the [dockerclient](https://github.com/fsouza/go-dockerclient/blob/01804dec8a84d0a77e63611f2b62d33e9bb2b64a/signal.go) + log the output(stdout/stderr) of notify command -notify-sighup container-ID - send HUP signal to container. Equivalent to 'docker kill -s HUP container-ID', or `-notify-container container-ID -notify-signal 1` + send HUP signal to container. Equivalent to docker kill -s HUP container-ID + -notify-signal int + signal to send to the notify-container. Defaults to SIGHUP (default 1) -only-exposed - only include containers with exposed ports + only include containers with exposed ports -only-published - only include containers with published ports (implies -only-exposed) - -include-stopped - include stopped containers + only include containers with published ports (implies -only-exposed) -tlscacert string - path to TLS CA certificate file (default "/Users/jason/.docker/machine/machines/default/ca.pem") + path to TLS CA certificate file (default "/root/.docker/ca.pem") -tlscert string - path to TLS client certificate file (default "/Users/jason/.docker/machine/machines/default/cert.pem") + path to TLS client certificate file (default "/root/.docker/cert.pem") -tlskey string - path to TLS client key file (default "/Users/jason/.docker/machine/machines/default/key.pem") + path to TLS client key file (default "/root/.docker/key.pem") -tlsverify - verify docker daemon's TLS certicate (default true) + verify docker daemon's TLS certicate -version - show version + show version + -wait string + minimum and maximum durations to wait (e.g. "500ms:2s") before triggering generate + -wasmcache string + path to cache directory for compiled wasm modules (default "/tmp/docker-gen-wasm-cache") -watch - watch for container changes - -wait - minimum (and/or maximum) duration to wait after each container change before triggering + watch for container changes Arguments: template - path to a template to generate - dest - path to write the template. If not specfied, STDOUT is used + dest - path to a write the template. If not specfied, STDOUT is used Environment Variables: DOCKER_HOST - default value for -endpoint - DOCKER_CERT_PATH - directory path containing key.pem, cert.pm and ca.pem - DOCKER_TLS_VERIFY - enable client TLS verification] + DOCKER_CERT_PATH - directory path containing key.pem, cert.pem and ca.pem + DOCKER_TLS_VERIFY - enable client TLS verification + WASM_CACHE_DIR - path to cache directory for compiled wasm modules, default for -wasmcache ``` If no `` file is specified, the output is sent to stdout. Mainly useful for debugging. @@ -404,6 +406,67 @@ For example, this is a JSON version of an emitted RuntimeContainer struct: * *`whereLabelExists $containers $label`*: Filters a slice of containers based on the existence of the label `$label`. * *`whereLabelDoesNotExist $containers $label`*: Filters a slice of containers based on the non-existence of the label `$label`. * *`whereLabelValueMatches $containers $label $pattern`*: Filters a slice of containers based on the existence of the label `$label` with values matching the regular expression `$pattern`. + +=== + +### Templating via WASM modules + +`docker-gen` can run arbitrary Webassembly (WASI) modules. The module takes json-serialized representation of docker containers on its standard input (`os.Stdin`), emits a file on standard output (`os.Stdout`) and can output messages direcly on `docker-gen`'s standatd error stream (`os.Stderr`). You can develop your own wasm module using `github.com/nginx-proxy/docker-gen/plugin` library. It automates parsing operations and emits output and errors. It also configures Go's standard `log` module to output on `os.Stderr`, so it can be used to show errors and warnings. WASM plugin module receives the following data structure: + +```go +type PluginContext struct { + Containers []*RuntimeContainer + Env map[string]string + Docker Docker +} +``` + +All other structures are the same as shown for the templates. + +Here is a simple example of a plugin module: + + +```go +// file main.go +package main + +import ( + "bytes" + "fmt" + + "github.com/nginx-proxy/docker-gen/plugin" +) + + +func main() { + plugin.Main(func(in *plugin.PluginContext) ([]byte, error) { + bout := bytes.Buffer{} + for _, container := range in.Containers { + bout.WriteString(fmt.Sprintf( + "Container %s is runnning: %t(%s)\n", + container.Name, + container.State.Running, + container.State.Health.Status)) + } + return bout.Bytes(), nil + }) +} +``` + +You can compile it to webassembly with tinygo: + +```shell +tinygo build -o ./docker-ps-like.wasm -target wasi main.go +``` + +After compilation you can use the compiled plugin as a template: + +```shell +docker-gen ./docker-ps-like.wasm +``` + +A more sophisticated example of a webassembly plugin module can be found [here](/examples/nginx-wasm-example/). It outputs the same content as [`nginx.tmpl`](/templates/nginx.tmpl), but provides several warnings on misconfiguration of `VIRTUAL_HOST` and `VIRTUAL_PORT` variables on standart errors (`os.Stderr`). + === diff --git a/cmd/docker-gen/main.go b/cmd/docker-gen/main.go index a28deeac..8d3b23d7 100644 --- a/cmd/docker-gen/main.go +++ b/cmd/docker-gen/main.go @@ -13,6 +13,7 @@ import ( docker "github.com/fsouza/go-dockerclient" "github.com/nginx-proxy/docker-gen/internal/config" "github.com/nginx-proxy/docker-gen/internal/generator" + "github.com/nginx-proxy/docker-gen/internal/template" ) type stringslice []string @@ -38,6 +39,7 @@ var ( tlsKey string tlsCaCert string tlsVerify bool + wasmCacheDir *string = &template.WasmCacheDir ) func (strings *stringslice) String() string { @@ -68,6 +70,7 @@ Environment Variables: DOCKER_HOST - default value for -endpoint DOCKER_CERT_PATH - directory path containing key.pem, cert.pem and ca.pem DOCKER_TLS_VERIFY - enable client TLS verification + WASM_CACHE_DIR - path to cache directory for compiled wasm modules, default for -wasmcache `) println(`For more information, see https://github.com/nginx-proxy/docker-gen`) } @@ -109,6 +112,11 @@ func initFlags() { flag.StringVar(&tlsCert, "tlscert", filepath.Join(certPath, "cert.pem"), "path to TLS client certificate file") flag.StringVar(&tlsKey, "tlskey", filepath.Join(certPath, "key.pem"), "path to TLS client key file") flag.StringVar(&tlsCaCert, "tlscacert", filepath.Join(certPath, "ca.pem"), "path to TLS CA certificate file") + wasmCacheEnv := os.Getenv("WASM_CACHE_DIR") + if wasmCacheEnv != "" { + *wasmCacheDir = wasmCacheEnv + } + flag.StringVar(wasmCacheDir, "wasmcache", *wasmCacheDir, "path to cache directory for compiled wasm modules") flag.BoolVar(&tlsVerify, "tlsverify", os.Getenv("DOCKER_TLS_VERIFY") != "", "verify docker daemon's TLS certicate") flag.Usage = usage diff --git a/examples/nginx-wasm-example/go.mod b/examples/nginx-wasm-example/go.mod new file mode 100644 index 00000000..263b2735 --- /dev/null +++ b/examples/nginx-wasm-example/go.mod @@ -0,0 +1,12 @@ +module github.com/nginx-proxy/docker-gen/nginx-wasm-example + +go 1.21 + +require github.com/nginx-proxy/docker-gen/plugin v0.0.0-00010101000000-000000000000 + +require ( + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect +) + +replace github.com/nginx-proxy/docker-gen/plugin => ../../plugin diff --git a/examples/nginx-wasm-example/go.sum b/examples/nginx-wasm-example/go.sum new file mode 100644 index 00000000..7707cb6a --- /dev/null +++ b/examples/nginx-wasm-example/go.sum @@ -0,0 +1,4 @@ +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= diff --git a/examples/nginx-wasm-example/main.go b/examples/nginx-wasm-example/main.go new file mode 100644 index 00000000..6e6175f4 --- /dev/null +++ b/examples/nginx-wasm-example/main.go @@ -0,0 +1,131 @@ +package main + +import ( + "bytes" + "log" + "os" + "strings" + + "github.com/nginx-proxy/docker-gen/plugin" +) + +var defaultHttpServer = ` +server { + listen 80 default_server; + server_name _; # This is just an invalid value which will never trigger on a real hostname. + error_log /proc/self/fd/2; + access_log /proc/self/fd/1; + return 503; +} +` + +var upstreamServer = ` + # ${NAME} + server ${IP}:${PORT}; +` + +var proxiedServer = ` +upstream ${HOST} { +${UPSTREAMS} +} + +server { + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + server_name ${HOST}; + proxy_buffering off; + error_log /proc/self/fd/2; + access_log /proc/self/fd/1; + + location / { + proxy_pass ${HOST}; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # HTTP 1.1 support + proxy_http_version 1.1; + proxy_set_header Connection ""; + } +} +` + +func virtualhost(t *plugin.RuntimeContainer) (*string, error) { + if s, ok := t.Env["VIRTUAL_HOST"]; ok { + return &s, nil + } + return nil, nil +} + +func findAddressWithPort(c *plugin.RuntimeContainer, port string) bool { + for _, address := range c.Addresses { + if address.Port == port { + return true + } + } + return false +} + +func NginxGen(in *plugin.PluginContext) (out []byte, err error) { + bout := bytes.NewBuffer(make([]byte, 4096)) + bout.WriteString(defaultHttpServer) + + containersWithHosts, err := plugin.GroupByMulti(in.Containers, virtualhost, ",") + if err != nil { + return + } + for host, containers := range containersWithHosts { + upstreams := make([]string, 0, len(containers)) + for _, c := range containers { + if c.State.Health.Status != "" && c.State.Health.Status != "healthy" { + log.Printf("Container %s is unhealthy", c.Name) + continue + } + if len(c.Networks) == 0 { + log.Printf("Container %s has no networks, but it is a virtual host %s", c.Name, host) + continue + } + vars := map[string]string{"NAME": c.Name, "IP": c.Networks[0].IP, "PORT": ""} + if len(c.Addresses) == 1 { + // If only 1 port exposed, use that + vars["PORT"] = c.Addresses[0].Port + } else if port, ok := c.Env["VIRTUAL_PORT"]; ok { + if findAddressWithPort(c, port) { + vars["PORT"] = port + } else { + log.Printf("Container %s (vhost %s) has VIRTUAL_PORT %s, but it does not expose it", c.Name, host, port) + continue + } + } else { + // Else default to standard web port 80 + port := "80" + if findAddressWithPort(c, port) { + vars["PORT"] = port + } else { + log.Printf("Container %s (vhost %s) does not declare VIRTUAL_PORT, exposes multiple ports and does not expose port 80", c.Name, host) + continue + } + } + upstreams = append(upstreams, os.Expand(upstreamServer, func(s string) string { return vars[s] })) + } + expanded := os.Expand(proxiedServer, func(s string) string { + switch s { + case "HOST": + return host + case "UPSTREAMS": + return strings.Join(upstreams, "\n") + default: + return "$" + s + } + }) + bout.WriteString(expanded) + } + + out = bout.Bytes() + return +} + +func main() { + plugin.Main(NginxGen) +} diff --git a/examples/nginx-wasm-example/nginx-wasm-example.wasm b/examples/nginx-wasm-example/nginx-wasm-example.wasm new file mode 100755 index 00000000..748c8c95 Binary files /dev/null and b/examples/nginx-wasm-example/nginx-wasm-example.wasm differ diff --git a/go.mod b/go.mod index 3203a180..9c34d22f 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,13 @@ require ( github.com/BurntSushi/toml v1.3.2 github.com/Masterminds/sprig/v3 v3.2.3 github.com/fsouza/go-dockerclient v1.10.2 + github.com/nginx-proxy/docker-gen/plugin v0.0.0-00010101000000-000000000000 github.com/stretchr/testify v1.8.4 + github.com/tetratelabs/wazero v1.6.0 ) +replace github.com/nginx-proxy/docker-gen/plugin => ./plugin + require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -25,8 +29,10 @@ require ( github.com/gorilla/mux v1.8.1 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.12 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/compress v1.15.9 // indirect github.com/kr/text v0.2.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect diff --git a/go.sum b/go.sum index da5ee197..88e3921d 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= @@ -56,6 +58,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= @@ -94,6 +98,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g= +github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/go.work b/go.work new file mode 100644 index 00000000..76437626 --- /dev/null +++ b/go.work @@ -0,0 +1,5 @@ +go 1.21 + +use . +use plugin +use examples/nginx-wasm-example \ No newline at end of file diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 00000000..f744f72e --- /dev/null +++ b/go.work.sum @@ -0,0 +1,100 @@ +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.1.6/go.mod h1:BWtoWl5ghVymxu6MBjg79W9NZrCRyHIdUtk4cauMe34= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.1.4/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containernetworking/cni v1.1.1/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= +github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8= +github.com/containers/ocicrypt v1.1.3/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pABH85425Es2g= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= +github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= +go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= +go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= +go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= +go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= +k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= +k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= +k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= +k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= +k8s.io/cri-api v0.25.0/go.mod h1:J1rAyQkSJ2Q6I+aBMOVgg2/cbbebso6FNa0UagiR0kc= +k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/internal/template/template.go b/internal/template/template.go index 2ee485f6..2ced3169 100644 --- a/internal/template/template.go +++ b/internal/template/template.go @@ -161,7 +161,12 @@ func GenerateFile(config config.Config, containers context.Context) bool { filteredContainers = filteredRunningContainers } - contents := executeTemplate(config.Template, filteredContainers) + var contents []byte + if filepath.Ext(config.Template) == ".wasm" { + contents = executeWasm(config.Template, filteredContainers) + } else { + contents = executeTemplate(config.Template, filteredContainers) + } if !config.KeepBlankLines { buf := new(bytes.Buffer) diff --git a/internal/template/wasm.go b/internal/template/wasm.go new file mode 100644 index 00000000..174fbece --- /dev/null +++ b/internal/template/wasm.go @@ -0,0 +1,205 @@ +package template + +import ( + "bytes" + goctx "context" + "log" + "os" + "path/filepath" + + "github.com/nginx-proxy/docker-gen/internal/context" + "github.com/nginx-proxy/docker-gen/plugin" + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" + "github.com/tetratelabs/wazero/sys" +) + +var WasmCacheDir = filepath.Join(os.TempDir(), "docker-gen-wasm-cache") + +func executeWasm(wasmFile string, containers context.Context) []byte { + outBuf := bytes.Buffer{} + + inBytes, err := pluginInputFromContext(containers).MarshalJSON() + if err != nil { + log.Fatalf("Unable to serialize plugin input: %s", err.Error()) + } + inBuf := bytes.NewBuffer(inBytes) + + ctx := goctx.Background() + + wasmBytes, err := os.ReadFile(wasmFile) + if err != nil { + log.Fatalf("Unable to parse template: %s", err.Error()) + } + + wazeroCache, err := wazero.NewCompilationCacheWithDir(WasmCacheDir) + if err != nil { + log.Fatalf("wazero.NewCompilationCacheWithDir: %s", err.Error()) + } + + err = os.MkdirAll(WasmCacheDir, 0700) + if err != nil { + log.Fatalf("Unable to create WasmCacheDir: %s", err.Error()) + } + config := wazero.NewRuntimeConfig().WithCompilationCache(wazeroCache) + rt := wazero.NewRuntimeWithConfig(ctx, config) + + if _, err := wasi_snapshot_preview1.Instantiate(ctx, rt); err != nil { + log.Fatalf("wasi_snapshot_preview1 instantiate: %s", err.Error()) + } + + // Compile the Wasm binary once so that we can skip the entire compilation + // time during instantiation. + code, err := rt.CompileModule(ctx, wasmBytes) + if err != nil { + log.Fatalf("compile module: %s", err.Error()) + } + + if _, err = rt.InstantiateModule(ctx, + code, + wazero.NewModuleConfig(). + WithStdin(inBuf). + WithStdout(&outBuf). + WithStderr(os.Stderr). + WithSysWalltime(). + WithArgs("gen.wasm"), + ); err != nil { + // Note: Most compilers do not exit the module after running "_start", + // unless there was an error. This allows you to call exported functions. + if exitErr, ok := err.(*sys.ExitError); ok && exitErr.ExitCode() != 0 { + log.Panicf("exit_code: %d\n", exitErr.ExitCode()) + } else if !ok { + log.Panicf("Failed to run %s", err) + } + } + + return outBuf.Bytes() +} + +func pluginNetwork(in *context.Network) (ret plugin.Network) { + ret.IP = in.IP + ret.Name = in.Name + ret.Gateway = in.Gateway + ret.EndpointID = in.EndpointID + ret.IPv6Gateway = in.IPv6Gateway + ret.GlobalIPv6Address = in.GlobalIPv6Address + ret.MacAddress = in.MacAddress + ret.GlobalIPv6PrefixLen = in.GlobalIPv6PrefixLen + ret.IPPrefixLen = in.IPPrefixLen + ret.Internal = in.Internal + return +} + +func pluginVolume(in *context.Volume) (ret plugin.Volume) { + ret.Path = in.Path + ret.HostPath = in.HostPath + ret.ReadWrite = in.ReadWrite + return +} + +func pluginState(in *context.State) (ret plugin.State) { + ret.Running = in.Running + ret.Health = pluginHealth(&in.Health) + return +} + +func pluginHealth(in *context.Health) (ret plugin.Health) { + ret.Status = in.Status + return +} + +func pluginAddress(in *context.Address) (ret plugin.Address) { + ret.IP = in.IP + ret.IP6LinkLocal = in.IP6LinkLocal + ret.IP6Global = in.IP6Global + ret.Port = in.Port + ret.HostPort = in.HostPort + ret.Proto = in.Proto + ret.HostIP = in.HostIP + return +} + +func pluginRuntimeContainer(in *context.RuntimeContainer) (ret *plugin.RuntimeContainer) { + ret = new(plugin.RuntimeContainer) + ret.ID = in.ID + ret.Created = in.Created + ret.Addresses = make([]plugin.Address, 0, len(in.Addresses)) + for _, v := range in.Addresses { + ret.Addresses = append(ret.Addresses, pluginAddress(&v)) + } + ret.Networks = make([]plugin.Network, 0, len(in.Networks)) + for _, v := range in.Networks { + ret.Networks = append(ret.Networks, pluginNetwork(&v)) + } + ret.Gateway = in.Gateway + ret.Name = in.Name + ret.Hostname = in.Hostname + ret.NetworkMode = in.NetworkMode + ret.Image = pluginDockerImage(&in.Image) + ret.Env = in.Env + ret.Volumes = make(map[string]plugin.Volume) + for k, v := range in.Volumes { + ret.Volumes[k] = pluginVolume(&v) + } + ret.Node = pluginSwarmNode(&in.Node) + ret.Labels = in.Labels + ret.IP = in.IP + ret.IP6LinkLocal = in.IP6LinkLocal + ret.IP6Global = in.IP6Global + ret.Mounts = make([]plugin.Mount, 0, len(in.Mounts)) + for _, v := range in.Mounts { + ret.Mounts = append(ret.Mounts, pluginMount(&v)) + } + ret.State = pluginState(&in.State) + return +} + +func pluginDockerImage(in *context.DockerImage) (ret plugin.DockerImage) { + ret.Registry = in.Registry + ret.Repository = in.Repository + ret.Tag = in.Tag + return +} + +func pluginSwarmNode(in *context.SwarmNode) (ret plugin.SwarmNode) { + ret.ID = in.ID + ret.Name = in.Name + ret.Address = pluginAddress(&in.Address) + return +} + +func pluginMount(in *context.Mount) (ret plugin.Mount) { + ret.Name = in.Name + ret.Source = in.Source + ret.Destination = in.Destination + ret.Driver = in.Driver + ret.Mode = in.Mode + ret.RW = in.RW + return +} + +func pluginDocker(in *context.Docker) (ret plugin.Docker) { + ret.Name = in.Name + ret.NumContainers = in.NumContainers + ret.NumImages = in.NumImages + ret.Version = in.Version + ret.ApiVersion = in.ApiVersion + ret.GoVersion = in.GoVersion + ret.OperatingSystem = in.OperatingSystem + ret.Architecture = in.Architecture + ret.CurrentContainerID = in.CurrentContainerID + return +} + +func pluginInputFromContext(ctx context.Context) *plugin.PluginContext { + containers := make([]*plugin.RuntimeContainer, 0, len(ctx)) + for _, c := range ctx { + containers = append(containers, pluginRuntimeContainer(c)) + } + d := ctx.Docker() + return &plugin.PluginContext{ + Containers: containers, + Env: ctx.Env(), + Docker: pluginDocker(&d), + } +} diff --git a/plugin/go.mod b/plugin/go.mod new file mode 100644 index 00000000..de462845 --- /dev/null +++ b/plugin/go.mod @@ -0,0 +1,7 @@ +module github.com/nginx-proxy/docker-gen/plugin + +go 1.21 + +require github.com/mailru/easyjson v0.7.7 + +require github.com/josharian/intern v1.0.0 // indirect diff --git a/plugin/go.sum b/plugin/go.sum new file mode 100644 index 00000000..7707cb6a --- /dev/null +++ b/plugin/go.sum @@ -0,0 +1,4 @@ +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= diff --git a/plugin/plugin_groupby.go b/plugin/plugin_groupby.go new file mode 100644 index 00000000..385e5e59 --- /dev/null +++ b/plugin/plugin_groupby.go @@ -0,0 +1,73 @@ +package plugin + +import "strings" + +type KeyFunc[T any, Value any] func(*T) (*Value, error) + +// Generalized groupBy function +func GeneralizedGroupBy[T any, Value any]( + entries []*T, + key KeyFunc[T, Value], + addEntry func(map[string][]*T, Value, *T), +) ( + map[string][]*T, error, +) { + groups := make(map[string][]*T) + for i := 0; i < len(entries); i++ { + v := entries[i] + value, err := key(v) + if err != nil { + return nil, err + } + if value != nil { + addEntry(groups, *value, v) + } + } + return groups, nil +} + +func GroupByMulti[T any](entries []*T, key KeyFunc[T, string], sep string) (map[string][]*T, error) { + return GeneralizedGroupBy(entries, key, func(groups map[string][]*T, value string, v *T) { + items := strings.Split(value, sep) + for _, item := range items { + groups[item] = append(groups[item], v) + } + }) +} + +// // groupBy groups a generic array or slice by the path property key +// func groupBy(entries interface{}, key string) (map[string][]interface{}, error) { +// return generalizedGroupBy("groupBy", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) { +// groups[value.(string)] = append(groups[value.(string)], v) +// }) +// } + +// // groupByKeys is the same as groupBy but only returns a list of keys +// func groupByKeys(entries interface{}, key string) ([]string, error) { +// keys, err := generalizedGroupByKey("groupByKeys", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) { +// groups[value.(string)] = append(groups[value.(string)], v) +// }) + +// if err != nil { +// return nil, err +// } + +// ret := []string{} +// for k := range keys { +// ret = append(ret, k) +// } +// return ret, nil +// } + +// GroupByLabel is the same as groupBy but over a given label +func GroupByLabel(entries []*RuntimeContainer, label string) (map[string][]*RuntimeContainer, error) { + getLabel := func(container *RuntimeContainer) (*string, error) { + if value, ok := container.Labels[label]; ok { + return &value, nil + } + return nil, nil + } + return GeneralizedGroupBy(entries, getLabel, func(groups map[string][]*RuntimeContainer, value string, v *RuntimeContainer) { + groups[value] = append(groups[value], v) + }) +} diff --git a/plugin/plugin_main.go b/plugin/plugin_main.go new file mode 100644 index 00000000..39759f5d --- /dev/null +++ b/plugin/plugin_main.go @@ -0,0 +1,30 @@ +package plugin + +import ( + "io" + "log" + "os" + + "github.com/mailru/easyjson" +) + +func Main(m func(in *PluginContext) (out []byte, err error)) { + log.SetOutput(os.Stderr) + + rawBytes, err := io.ReadAll(os.Stdin) + if err != nil { + log.Fatal(err) + } + + inp := PluginContext{} + err = easyjson.Unmarshal(rawBytes, &inp) + if err != nil { + log.Fatal(err) + } + + rawBytes, err = m(&inp) + if err != nil { + log.Fatal(err) + } + os.Stdout.Write(rawBytes) +} diff --git a/plugin/plugin_structs.go b/plugin/plugin_structs.go new file mode 100644 index 00000000..a9cd4ece --- /dev/null +++ b/plugin/plugin_structs.go @@ -0,0 +1,105 @@ +package plugin + +//go:generate $GOPATH/bin/easyjson -all $GOFILE + +import "time" + +type ( + PluginContext struct { + Containers []*RuntimeContainer + Env map[string]string + Docker Docker + } + + Network struct { + IP string + Name string + Gateway string + EndpointID string + IPv6Gateway string + GlobalIPv6Address string + MacAddress string + GlobalIPv6PrefixLen int + IPPrefixLen int + Internal bool + } + + Volume struct { + Path string + HostPath string + ReadWrite bool + } + + State struct { + Running bool + Health Health + } + + Health struct { + Status string + } + + Address struct { + IP string + IP6LinkLocal string + IP6Global string + Port string + HostPort string + Proto string + HostIP string + } + + RuntimeContainer struct { + ID string + Created time.Time + Addresses []Address + Networks []Network + Gateway string + Name string + Hostname string + NetworkMode string + Image DockerImage + Env map[string]string + Volumes map[string]Volume + Node SwarmNode + Labels map[string]string + IP string + IP6LinkLocal string + IP6Global string + Mounts []Mount + State State + } + + DockerImage struct { + Registry string + Repository string + Tag string + } + + SwarmNode struct { + ID string + Name string + Address Address + } + + Mount struct { + Name string + Source string + Destination string + Driver string + Mode string + RW bool + } + + Docker struct { + Name string + NumContainers int + NumImages int + Version string + ApiVersion string + GoVersion string + OperatingSystem string + Architecture string + CurrentContainerID string + } +) diff --git a/plugin/plugin_structs_easyjson.go b/plugin/plugin_structs_easyjson.go new file mode 100644 index 00000000..7cd235b8 --- /dev/null +++ b/plugin/plugin_structs_easyjson.go @@ -0,0 +1,1385 @@ +// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. + +package plugin + +import ( + json "encoding/json" + easyjson "github.com/mailru/easyjson" + jlexer "github.com/mailru/easyjson/jlexer" + jwriter "github.com/mailru/easyjson/jwriter" +) + +// suppress unused package warning +var ( + _ *json.RawMessage + _ *jlexer.Lexer + _ *jwriter.Writer + _ easyjson.Marshaler +) + +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin(in *jlexer.Lexer, out *Volume) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Path": + out.Path = string(in.String()) + case "HostPath": + out.HostPath = string(in.String()) + case "ReadWrite": + out.ReadWrite = bool(in.Bool()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin(out *jwriter.Writer, in Volume) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Path\":" + out.RawString(prefix[1:]) + out.String(string(in.Path)) + } + { + const prefix string = ",\"HostPath\":" + out.RawString(prefix) + out.String(string(in.HostPath)) + } + { + const prefix string = ",\"ReadWrite\":" + out.RawString(prefix) + out.Bool(bool(in.ReadWrite)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Volume) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Volume) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Volume) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Volume) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin(l, v) +} +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin1(in *jlexer.Lexer, out *SwarmNode) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "ID": + out.ID = string(in.String()) + case "Name": + out.Name = string(in.String()) + case "Address": + (out.Address).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin1(out *jwriter.Writer, in SwarmNode) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"ID\":" + out.RawString(prefix[1:]) + out.String(string(in.ID)) + } + { + const prefix string = ",\"Name\":" + out.RawString(prefix) + out.String(string(in.Name)) + } + { + const prefix string = ",\"Address\":" + out.RawString(prefix) + (in.Address).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v SwarmNode) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin1(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SwarmNode) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin1(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *SwarmNode) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin1(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SwarmNode) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin1(l, v) +} +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin2(in *jlexer.Lexer, out *State) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Running": + out.Running = bool(in.Bool()) + case "Health": + (out.Health).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin2(out *jwriter.Writer, in State) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Running\":" + out.RawString(prefix[1:]) + out.Bool(bool(in.Running)) + } + { + const prefix string = ",\"Health\":" + out.RawString(prefix) + (in.Health).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v State) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin2(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v State) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin2(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *State) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin2(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *State) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin2(l, v) +} +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin3(in *jlexer.Lexer, out *RuntimeContainer) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "ID": + out.ID = string(in.String()) + case "Created": + if data := in.Raw(); in.Ok() { + in.AddError((out.Created).UnmarshalJSON(data)) + } + case "Addresses": + if in.IsNull() { + in.Skip() + out.Addresses = nil + } else { + in.Delim('[') + if out.Addresses == nil { + if !in.IsDelim(']') { + out.Addresses = make([]Address, 0, 0) + } else { + out.Addresses = []Address{} + } + } else { + out.Addresses = (out.Addresses)[:0] + } + for !in.IsDelim(']') { + var v1 Address + (v1).UnmarshalEasyJSON(in) + out.Addresses = append(out.Addresses, v1) + in.WantComma() + } + in.Delim(']') + } + case "Networks": + if in.IsNull() { + in.Skip() + out.Networks = nil + } else { + in.Delim('[') + if out.Networks == nil { + if !in.IsDelim(']') { + out.Networks = make([]Network, 0, 0) + } else { + out.Networks = []Network{} + } + } else { + out.Networks = (out.Networks)[:0] + } + for !in.IsDelim(']') { + var v2 Network + (v2).UnmarshalEasyJSON(in) + out.Networks = append(out.Networks, v2) + in.WantComma() + } + in.Delim(']') + } + case "Gateway": + out.Gateway = string(in.String()) + case "Name": + out.Name = string(in.String()) + case "Hostname": + out.Hostname = string(in.String()) + case "NetworkMode": + out.NetworkMode = string(in.String()) + case "Image": + (out.Image).UnmarshalEasyJSON(in) + case "Env": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + out.Env = make(map[string]string) + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v3 string + v3 = string(in.String()) + (out.Env)[key] = v3 + in.WantComma() + } + in.Delim('}') + } + case "Volumes": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + out.Volumes = make(map[string]Volume) + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v4 Volume + (v4).UnmarshalEasyJSON(in) + (out.Volumes)[key] = v4 + in.WantComma() + } + in.Delim('}') + } + case "Node": + (out.Node).UnmarshalEasyJSON(in) + case "Labels": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + out.Labels = make(map[string]string) + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v5 string + v5 = string(in.String()) + (out.Labels)[key] = v5 + in.WantComma() + } + in.Delim('}') + } + case "IP": + out.IP = string(in.String()) + case "IP6LinkLocal": + out.IP6LinkLocal = string(in.String()) + case "IP6Global": + out.IP6Global = string(in.String()) + case "Mounts": + if in.IsNull() { + in.Skip() + out.Mounts = nil + } else { + in.Delim('[') + if out.Mounts == nil { + if !in.IsDelim(']') { + out.Mounts = make([]Mount, 0, 0) + } else { + out.Mounts = []Mount{} + } + } else { + out.Mounts = (out.Mounts)[:0] + } + for !in.IsDelim(']') { + var v6 Mount + (v6).UnmarshalEasyJSON(in) + out.Mounts = append(out.Mounts, v6) + in.WantComma() + } + in.Delim(']') + } + case "State": + (out.State).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin3(out *jwriter.Writer, in RuntimeContainer) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"ID\":" + out.RawString(prefix[1:]) + out.String(string(in.ID)) + } + { + const prefix string = ",\"Created\":" + out.RawString(prefix) + out.Raw((in.Created).MarshalJSON()) + } + { + const prefix string = ",\"Addresses\":" + out.RawString(prefix) + if in.Addresses == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v7, v8 := range in.Addresses { + if v7 > 0 { + out.RawByte(',') + } + (v8).MarshalEasyJSON(out) + } + out.RawByte(']') + } + } + { + const prefix string = ",\"Networks\":" + out.RawString(prefix) + if in.Networks == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v9, v10 := range in.Networks { + if v9 > 0 { + out.RawByte(',') + } + (v10).MarshalEasyJSON(out) + } + out.RawByte(']') + } + } + { + const prefix string = ",\"Gateway\":" + out.RawString(prefix) + out.String(string(in.Gateway)) + } + { + const prefix string = ",\"Name\":" + out.RawString(prefix) + out.String(string(in.Name)) + } + { + const prefix string = ",\"Hostname\":" + out.RawString(prefix) + out.String(string(in.Hostname)) + } + { + const prefix string = ",\"NetworkMode\":" + out.RawString(prefix) + out.String(string(in.NetworkMode)) + } + { + const prefix string = ",\"Image\":" + out.RawString(prefix) + (in.Image).MarshalEasyJSON(out) + } + { + const prefix string = ",\"Env\":" + out.RawString(prefix) + if in.Env == nil && (out.Flags&jwriter.NilMapAsEmpty) == 0 { + out.RawString(`null`) + } else { + out.RawByte('{') + v11First := true + for v11Name, v11Value := range in.Env { + if v11First { + v11First = false + } else { + out.RawByte(',') + } + out.String(string(v11Name)) + out.RawByte(':') + out.String(string(v11Value)) + } + out.RawByte('}') + } + } + { + const prefix string = ",\"Volumes\":" + out.RawString(prefix) + if in.Volumes == nil && (out.Flags&jwriter.NilMapAsEmpty) == 0 { + out.RawString(`null`) + } else { + out.RawByte('{') + v12First := true + for v12Name, v12Value := range in.Volumes { + if v12First { + v12First = false + } else { + out.RawByte(',') + } + out.String(string(v12Name)) + out.RawByte(':') + (v12Value).MarshalEasyJSON(out) + } + out.RawByte('}') + } + } + { + const prefix string = ",\"Node\":" + out.RawString(prefix) + (in.Node).MarshalEasyJSON(out) + } + { + const prefix string = ",\"Labels\":" + out.RawString(prefix) + if in.Labels == nil && (out.Flags&jwriter.NilMapAsEmpty) == 0 { + out.RawString(`null`) + } else { + out.RawByte('{') + v13First := true + for v13Name, v13Value := range in.Labels { + if v13First { + v13First = false + } else { + out.RawByte(',') + } + out.String(string(v13Name)) + out.RawByte(':') + out.String(string(v13Value)) + } + out.RawByte('}') + } + } + { + const prefix string = ",\"IP\":" + out.RawString(prefix) + out.String(string(in.IP)) + } + { + const prefix string = ",\"IP6LinkLocal\":" + out.RawString(prefix) + out.String(string(in.IP6LinkLocal)) + } + { + const prefix string = ",\"IP6Global\":" + out.RawString(prefix) + out.String(string(in.IP6Global)) + } + { + const prefix string = ",\"Mounts\":" + out.RawString(prefix) + if in.Mounts == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v14, v15 := range in.Mounts { + if v14 > 0 { + out.RawByte(',') + } + (v15).MarshalEasyJSON(out) + } + out.RawByte(']') + } + } + { + const prefix string = ",\"State\":" + out.RawString(prefix) + (in.State).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v RuntimeContainer) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin3(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v RuntimeContainer) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin3(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *RuntimeContainer) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin3(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *RuntimeContainer) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin3(l, v) +} +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin4(in *jlexer.Lexer, out *PluginContext) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Containers": + if in.IsNull() { + in.Skip() + out.Containers = nil + } else { + in.Delim('[') + if out.Containers == nil { + if !in.IsDelim(']') { + out.Containers = make([]*RuntimeContainer, 0, 8) + } else { + out.Containers = []*RuntimeContainer{} + } + } else { + out.Containers = (out.Containers)[:0] + } + for !in.IsDelim(']') { + var v16 *RuntimeContainer + if in.IsNull() { + in.Skip() + v16 = nil + } else { + if v16 == nil { + v16 = new(RuntimeContainer) + } + (*v16).UnmarshalEasyJSON(in) + } + out.Containers = append(out.Containers, v16) + in.WantComma() + } + in.Delim(']') + } + case "Env": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + out.Env = make(map[string]string) + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v17 string + v17 = string(in.String()) + (out.Env)[key] = v17 + in.WantComma() + } + in.Delim('}') + } + case "Docker": + (out.Docker).UnmarshalEasyJSON(in) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin4(out *jwriter.Writer, in PluginContext) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Containers\":" + out.RawString(prefix[1:]) + if in.Containers == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v18, v19 := range in.Containers { + if v18 > 0 { + out.RawByte(',') + } + if v19 == nil { + out.RawString("null") + } else { + (*v19).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + { + const prefix string = ",\"Env\":" + out.RawString(prefix) + if in.Env == nil && (out.Flags&jwriter.NilMapAsEmpty) == 0 { + out.RawString(`null`) + } else { + out.RawByte('{') + v20First := true + for v20Name, v20Value := range in.Env { + if v20First { + v20First = false + } else { + out.RawByte(',') + } + out.String(string(v20Name)) + out.RawByte(':') + out.String(string(v20Value)) + } + out.RawByte('}') + } + } + { + const prefix string = ",\"Docker\":" + out.RawString(prefix) + (in.Docker).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v PluginContext) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin4(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v PluginContext) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin4(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *PluginContext) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin4(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *PluginContext) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin4(l, v) +} +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin5(in *jlexer.Lexer, out *Network) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "IP": + out.IP = string(in.String()) + case "Name": + out.Name = string(in.String()) + case "Gateway": + out.Gateway = string(in.String()) + case "EndpointID": + out.EndpointID = string(in.String()) + case "IPv6Gateway": + out.IPv6Gateway = string(in.String()) + case "GlobalIPv6Address": + out.GlobalIPv6Address = string(in.String()) + case "MacAddress": + out.MacAddress = string(in.String()) + case "GlobalIPv6PrefixLen": + out.GlobalIPv6PrefixLen = int(in.Int()) + case "IPPrefixLen": + out.IPPrefixLen = int(in.Int()) + case "Internal": + out.Internal = bool(in.Bool()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin5(out *jwriter.Writer, in Network) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"IP\":" + out.RawString(prefix[1:]) + out.String(string(in.IP)) + } + { + const prefix string = ",\"Name\":" + out.RawString(prefix) + out.String(string(in.Name)) + } + { + const prefix string = ",\"Gateway\":" + out.RawString(prefix) + out.String(string(in.Gateway)) + } + { + const prefix string = ",\"EndpointID\":" + out.RawString(prefix) + out.String(string(in.EndpointID)) + } + { + const prefix string = ",\"IPv6Gateway\":" + out.RawString(prefix) + out.String(string(in.IPv6Gateway)) + } + { + const prefix string = ",\"GlobalIPv6Address\":" + out.RawString(prefix) + out.String(string(in.GlobalIPv6Address)) + } + { + const prefix string = ",\"MacAddress\":" + out.RawString(prefix) + out.String(string(in.MacAddress)) + } + { + const prefix string = ",\"GlobalIPv6PrefixLen\":" + out.RawString(prefix) + out.Int(int(in.GlobalIPv6PrefixLen)) + } + { + const prefix string = ",\"IPPrefixLen\":" + out.RawString(prefix) + out.Int(int(in.IPPrefixLen)) + } + { + const prefix string = ",\"Internal\":" + out.RawString(prefix) + out.Bool(bool(in.Internal)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Network) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin5(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Network) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin5(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Network) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin5(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Network) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin5(l, v) +} +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin6(in *jlexer.Lexer, out *Mount) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Name": + out.Name = string(in.String()) + case "Source": + out.Source = string(in.String()) + case "Destination": + out.Destination = string(in.String()) + case "Driver": + out.Driver = string(in.String()) + case "Mode": + out.Mode = string(in.String()) + case "RW": + out.RW = bool(in.Bool()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin6(out *jwriter.Writer, in Mount) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Name\":" + out.RawString(prefix[1:]) + out.String(string(in.Name)) + } + { + const prefix string = ",\"Source\":" + out.RawString(prefix) + out.String(string(in.Source)) + } + { + const prefix string = ",\"Destination\":" + out.RawString(prefix) + out.String(string(in.Destination)) + } + { + const prefix string = ",\"Driver\":" + out.RawString(prefix) + out.String(string(in.Driver)) + } + { + const prefix string = ",\"Mode\":" + out.RawString(prefix) + out.String(string(in.Mode)) + } + { + const prefix string = ",\"RW\":" + out.RawString(prefix) + out.Bool(bool(in.RW)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Mount) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin6(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Mount) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin6(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Mount) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin6(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Mount) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin6(l, v) +} +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin7(in *jlexer.Lexer, out *Health) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Status": + out.Status = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin7(out *jwriter.Writer, in Health) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Status\":" + out.RawString(prefix[1:]) + out.String(string(in.Status)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Health) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin7(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Health) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin7(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Health) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin7(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Health) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin7(l, v) +} +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin8(in *jlexer.Lexer, out *DockerImage) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Registry": + out.Registry = string(in.String()) + case "Repository": + out.Repository = string(in.String()) + case "Tag": + out.Tag = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin8(out *jwriter.Writer, in DockerImage) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Registry\":" + out.RawString(prefix[1:]) + out.String(string(in.Registry)) + } + { + const prefix string = ",\"Repository\":" + out.RawString(prefix) + out.String(string(in.Repository)) + } + { + const prefix string = ",\"Tag\":" + out.RawString(prefix) + out.String(string(in.Tag)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v DockerImage) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin8(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v DockerImage) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin8(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *DockerImage) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin8(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *DockerImage) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin8(l, v) +} +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin9(in *jlexer.Lexer, out *Docker) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Name": + out.Name = string(in.String()) + case "NumContainers": + out.NumContainers = int(in.Int()) + case "NumImages": + out.NumImages = int(in.Int()) + case "Version": + out.Version = string(in.String()) + case "ApiVersion": + out.ApiVersion = string(in.String()) + case "GoVersion": + out.GoVersion = string(in.String()) + case "OperatingSystem": + out.OperatingSystem = string(in.String()) + case "Architecture": + out.Architecture = string(in.String()) + case "CurrentContainerID": + out.CurrentContainerID = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin9(out *jwriter.Writer, in Docker) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Name\":" + out.RawString(prefix[1:]) + out.String(string(in.Name)) + } + { + const prefix string = ",\"NumContainers\":" + out.RawString(prefix) + out.Int(int(in.NumContainers)) + } + { + const prefix string = ",\"NumImages\":" + out.RawString(prefix) + out.Int(int(in.NumImages)) + } + { + const prefix string = ",\"Version\":" + out.RawString(prefix) + out.String(string(in.Version)) + } + { + const prefix string = ",\"ApiVersion\":" + out.RawString(prefix) + out.String(string(in.ApiVersion)) + } + { + const prefix string = ",\"GoVersion\":" + out.RawString(prefix) + out.String(string(in.GoVersion)) + } + { + const prefix string = ",\"OperatingSystem\":" + out.RawString(prefix) + out.String(string(in.OperatingSystem)) + } + { + const prefix string = ",\"Architecture\":" + out.RawString(prefix) + out.String(string(in.Architecture)) + } + { + const prefix string = ",\"CurrentContainerID\":" + out.RawString(prefix) + out.String(string(in.CurrentContainerID)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Docker) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin9(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Docker) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin9(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Docker) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin9(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Docker) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin9(l, v) +} +func easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin10(in *jlexer.Lexer, out *Address) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "IP": + out.IP = string(in.String()) + case "IP6LinkLocal": + out.IP6LinkLocal = string(in.String()) + case "IP6Global": + out.IP6Global = string(in.String()) + case "Port": + out.Port = string(in.String()) + case "HostPort": + out.HostPort = string(in.String()) + case "Proto": + out.Proto = string(in.String()) + case "HostIP": + out.HostIP = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin10(out *jwriter.Writer, in Address) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"IP\":" + out.RawString(prefix[1:]) + out.String(string(in.IP)) + } + { + const prefix string = ",\"IP6LinkLocal\":" + out.RawString(prefix) + out.String(string(in.IP6LinkLocal)) + } + { + const prefix string = ",\"IP6Global\":" + out.RawString(prefix) + out.String(string(in.IP6Global)) + } + { + const prefix string = ",\"Port\":" + out.RawString(prefix) + out.String(string(in.Port)) + } + { + const prefix string = ",\"HostPort\":" + out.RawString(prefix) + out.String(string(in.HostPort)) + } + { + const prefix string = ",\"Proto\":" + out.RawString(prefix) + out.String(string(in.Proto)) + } + { + const prefix string = ",\"HostIP\":" + out.RawString(prefix) + out.String(string(in.HostIP)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Address) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin10(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Address) MarshalEasyJSON(w *jwriter.Writer) { + easyjson71062b96EncodeGithubComNginxProxyDockerGenPlugin10(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Address) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin10(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Address) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson71062b96DecodeGithubComNginxProxyDockerGenPlugin10(l, v) +}