mirror of
https://github.com/slimtoolkit/slim.git
synced 2025-06-03 04:00:23 +00:00
initial docker-compose support - wip
This commit is contained in:
parent
15d2bb599c
commit
5175e48ed0
27
go.mod
27
go.mod
@ -3,7 +3,7 @@ module github.com/docker-slim/docker-slim
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/Microsoft/hcsshim v0.8.9 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.22 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.5.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.2.0 // indirect
|
||||
github.com/antchfx/htmlquery v1.2.3 // indirect
|
||||
@ -14,42 +14,41 @@ require (
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect
|
||||
github.com/c-bata/go-prompt v0.2.3
|
||||
github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c
|
||||
github.com/containerd/containerd v1.3.4 // indirect
|
||||
github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/docker-slim/go-update v0.0.0-20190422071557-ed40247aff59
|
||||
github.com/docker-slim/uilive v0.0.2 // indirect
|
||||
github.com/docker-slim/uiprogress v0.0.0-20190505193231-9d4396e6d40b
|
||||
github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23
|
||||
github.com/docker/docker v20.10.8+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/dsnet/compress v0.0.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/fatih/color v1.10.0
|
||||
github.com/fsouza/go-dockerclient v1.6.5
|
||||
github.com/fsouza/go-dockerclient v1.7.4
|
||||
github.com/getkin/kin-openapi v0.19.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/gocolly/colly/v2 v2.0.1
|
||||
github.com/golang/protobuf v1.4.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/gosuri/uilive v0.0.3 // indirect
|
||||
github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214 // indirect
|
||||
github.com/klauspost/compress v1.10.6 // indirect
|
||||
github.com/klauspost/pgzip v1.2.4 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mattn/go-tty v0.0.3 // indirect
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/runc v1.0.2 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03 // indirect
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||
github.com/ulikunitz/xz v0.5.7 // indirect
|
||||
github.com/urfave/cli v1.22.4
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a // indirect
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||
golang.org/x/sys v0.0.0-20210921065528-437939a70204
|
||||
google.golang.org/appengine v1.6.6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200521103424-e9a78aa275b7 // indirect
|
||||
google.golang.org/grpc v1.29.1 // indirect
|
||||
github.com/compose-spec/compose-go v0.0.0-20210916141509-a7e1bc322970
|
||||
)
|
||||
|
||||
replace github.com/compose-spec/compose-go => ./pkg/third_party/compose-go
|
||||
|
@ -27,6 +27,12 @@ var CLI = cli.Command{
|
||||
commands.Cflag(commands.FlagTarget),
|
||||
commands.Cflag(commands.FlagPull),
|
||||
commands.Cflag(commands.FlagShowPullLogs),
|
||||
commands.Cflag(commands.FlagComposeFile),
|
||||
commands.Cflag(commands.FlagTargetComposeSvc),
|
||||
commands.Cflag(commands.FlagDepIncludeComposeSvc),
|
||||
commands.Cflag(commands.FlagDepExcludeComposeSvc),
|
||||
commands.Cflag(commands.FlagDepIncludeComposeSvcDeps),
|
||||
commands.Cflag(commands.FlagComposeNet),
|
||||
commands.Cflag(commands.FlagHTTPProbeOff),
|
||||
commands.Cflag(commands.FlagHTTPProbe),
|
||||
commands.Cflag(commands.FlagHTTPProbeCmd),
|
||||
@ -170,29 +176,41 @@ var CLI = cli.Command{
|
||||
xc.Exit(-1)
|
||||
}
|
||||
|
||||
composeFile := ctx.String(commands.FlagComposeFile)
|
||||
//todo: load/parse compose file and then use it to validate the related compose params
|
||||
targetComposeSvc := ctx.String(commands.FlagTargetComposeSvc)
|
||||
depIncludeComposeSvcDeps := ctx.String(commands.FlagDepIncludeComposeSvcDeps)
|
||||
depIncludeComposeSvcs := ctx.StringSlice(commands.FlagDepIncludeComposeSvc)
|
||||
depExcludeComposeSvcs := ctx.StringSlice(commands.FlagDepExcludeComposeSvc)
|
||||
composeNets := ctx.StringSlice(commands.FlagComposeNet)
|
||||
|
||||
var targetRef string
|
||||
|
||||
if cbOpts.Dockerfile == "" {
|
||||
targetRef = ctx.String(commands.FlagTarget)
|
||||
|
||||
if targetRef == "" {
|
||||
if len(ctx.Args()) < 1 {
|
||||
xc.Out.Error("param.target", "missing image ID/name")
|
||||
cli.ShowCommandHelp(ctx, Name)
|
||||
return nil
|
||||
} else {
|
||||
targetRef = ctx.Args().First()
|
||||
}
|
||||
}
|
||||
if composeFile != "" && targetComposeSvc != "" {
|
||||
targetRef = targetComposeSvc
|
||||
} else {
|
||||
targetRef = cbOpts.DockerfileContext
|
||||
if targetRef == "" {
|
||||
if len(ctx.Args()) < 1 {
|
||||
xc.Out.Error("param.target", "missing image ID/name")
|
||||
cli.ShowCommandHelp(ctx, Name)
|
||||
return nil
|
||||
} else {
|
||||
targetRef = ctx.Args().First()
|
||||
if cbOpts.Dockerfile == "" {
|
||||
targetRef = ctx.String(commands.FlagTarget)
|
||||
|
||||
if targetRef == "" {
|
||||
if len(ctx.Args()) < 1 {
|
||||
xc.Out.Error("param.target", "missing image ID/name")
|
||||
cli.ShowCommandHelp(ctx, Name)
|
||||
return nil
|
||||
} else {
|
||||
targetRef = ctx.Args().First()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
targetRef = cbOpts.DockerfileContext
|
||||
if targetRef == "" {
|
||||
if len(ctx.Args()) < 1 {
|
||||
xc.Out.Error("param.target", "missing Dockerfile build context directory")
|
||||
cli.ShowCommandHelp(ctx, Name)
|
||||
return nil
|
||||
} else {
|
||||
targetRef = ctx.Args().First()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -568,6 +586,12 @@ var CLI = cli.Command{
|
||||
targetRef,
|
||||
doPull,
|
||||
doShowPullLogs,
|
||||
composeFile,
|
||||
targetComposeSvc,
|
||||
depIncludeComposeSvcDeps,
|
||||
depIncludeComposeSvcs,
|
||||
depExcludeComposeSvcs,
|
||||
composeNets,
|
||||
cbOpts,
|
||||
crOpts,
|
||||
outputTags,
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"github.com/docker-slim/docker-slim/pkg/app"
|
||||
"github.com/docker-slim/docker-slim/pkg/app/master/builder"
|
||||
"github.com/docker-slim/docker-slim/pkg/app/master/commands"
|
||||
"github.com/docker-slim/docker-slim/pkg/app/master/compose"
|
||||
"github.com/docker-slim/docker-slim/pkg/app/master/config"
|
||||
"github.com/docker-slim/docker-slim/pkg/app/master/docker/dockerclient"
|
||||
"github.com/docker-slim/docker-slim/pkg/app/master/inspectors/container"
|
||||
@ -53,6 +54,12 @@ func OnCommand(
|
||||
targetRef string,
|
||||
doPull bool,
|
||||
doShowPullLogs bool,
|
||||
composeFile string,
|
||||
targetComposeSvc string,
|
||||
depIncludeComposeSvcDeps string,
|
||||
depIncludeComposeSvcs []string,
|
||||
depExcludeComposeSvcs []string,
|
||||
composeNets []string,
|
||||
cbOpts *config.ContainerBuildOptions,
|
||||
crOpts *config.ContainerRunOptions,
|
||||
outputTags []string,
|
||||
@ -150,9 +157,10 @@ func OnCommand(
|
||||
|
||||
xc.Out.State("started")
|
||||
|
||||
if cbOpts.Dockerfile == "" {
|
||||
if composeFile != "" && targetComposeSvc != "" {
|
||||
xc.Out.Info("params",
|
||||
ovars{
|
||||
"target.type": "compose.service",
|
||||
"target": targetRef,
|
||||
"continue.mode": continueAfter.Mode,
|
||||
"rt.as.user": doRunTargetAsUser,
|
||||
@ -160,14 +168,27 @@ func OnCommand(
|
||||
"tags": strings.Join(outputTags, ","),
|
||||
})
|
||||
} else {
|
||||
xc.Out.Info("params",
|
||||
ovars{
|
||||
"context": targetRef,
|
||||
"file": cbOpts.Dockerfile,
|
||||
"continue.mode": continueAfter.Mode,
|
||||
"rt.as.user": doRunTargetAsUser,
|
||||
"keep.perms": doKeepPerms,
|
||||
})
|
||||
if cbOpts.Dockerfile == "" {
|
||||
xc.Out.Info("params",
|
||||
ovars{
|
||||
"target.type": "image",
|
||||
"target": targetRef,
|
||||
"continue.mode": continueAfter.Mode,
|
||||
"rt.as.user": doRunTargetAsUser,
|
||||
"keep.perms": doKeepPerms,
|
||||
"tags": strings.Join(outputTags, ","),
|
||||
})
|
||||
} else {
|
||||
xc.Out.Info("params",
|
||||
ovars{
|
||||
"target.type": "dockerfile",
|
||||
"context": targetRef,
|
||||
"file": cbOpts.Dockerfile,
|
||||
"continue.mode": continueAfter.Mode,
|
||||
"rt.as.user": doRunTargetAsUser,
|
||||
"keep.perms": doKeepPerms,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if cbOpts.Dockerfile != "" {
|
||||
@ -261,6 +282,22 @@ func OnCommand(
|
||||
//todo: remove the temporary fat image (should have a flag for it in case users want the fat image too)
|
||||
}
|
||||
|
||||
if composeFile != "" {
|
||||
composeSelectors := compose.NewServiceSelectors(depIncludeComposeSvcDeps,
|
||||
depIncludeComposeSvcs,
|
||||
depExcludeComposeSvcs)
|
||||
|
||||
logger.Debugf("compose: file='%s' selectors='%+v'\n",
|
||||
composeFile, composeSelectors)
|
||||
/*
|
||||
cx, err := compose.NewExecution(xc,
|
||||
logger,
|
||||
client,
|
||||
composeFile
|
||||
)
|
||||
*/
|
||||
}
|
||||
|
||||
logger.Infof("image=%v http-probe=%v remove-file-artifacts=%v image-overrides=%+v entrypoint=%+v (%v) cmd=%+v (%v) workdir='%v' env=%+v expose=%+v",
|
||||
targetRef, doHTTPProbe, doRmFileArtifacts,
|
||||
imageOverrideSelectors,
|
||||
|
@ -14,6 +14,12 @@ var CommandSuggestion = prompt.Suggest{
|
||||
var CommandFlagSuggestions = &commands.FlagSuggestions{
|
||||
Names: []prompt.Suggest{
|
||||
{Text: commands.FullFlagName(commands.FlagTarget), Description: commands.FlagTargetUsage},
|
||||
{Text: commands.FullFlagName(commands.FlagComposeFile), Description: commands.FlagComposeFileUsage},
|
||||
{Text: commands.FullFlagName(commands.FlagTargetComposeSvc), Description: commands.FlagTargetComposeSvcUsage},
|
||||
{Text: commands.FullFlagName(commands.FlagDepIncludeComposeSvc), Description: commands.FlagDepIncludeComposeSvcUsage},
|
||||
{Text: commands.FullFlagName(commands.FlagDepExcludeComposeSvc), Description: commands.FlagDepExcludeComposeSvcUsage},
|
||||
{Text: commands.FullFlagName(commands.FlagDepIncludeComposeSvcDeps), Description: commands.FlagDepIncludeComposeSvcDepsUsage},
|
||||
{Text: commands.FullFlagName(commands.FlagComposeNet), Description: commands.FlagComposeNetUsage},
|
||||
{Text: commands.FullFlagName(commands.FlagPull), Description: commands.FlagPullUsage},
|
||||
{Text: commands.FullFlagName(commands.FlagShowPullLogs), Description: commands.FlagShowPullLogsUsage},
|
||||
{Text: commands.FullFlagName(FlagShowBuildLogs), Description: FlagShowBuildLogsUsage},
|
||||
@ -110,6 +116,7 @@ var CommandFlagSuggestions = &commands.FlagSuggestions{
|
||||
commands.FullFlagName(commands.FlagPull): commands.CompleteBool,
|
||||
commands.FullFlagName(commands.FlagShowPullLogs): commands.CompleteBool,
|
||||
commands.FullFlagName(commands.FlagTarget): commands.CompleteTarget,
|
||||
commands.FullFlagName(commands.FlagComposeFile): commands.CompleteFile,
|
||||
commands.FullFlagName(FlagShowBuildLogs): commands.CompleteBool,
|
||||
commands.FullFlagName(commands.FlagShowContainerLogs): commands.CompleteBool,
|
||||
commands.FullFlagName(commands.FlagPublishExposedPorts): commands.CompleteBool,
|
||||
|
@ -53,6 +53,14 @@ const (
|
||||
FlagPull = "pull"
|
||||
FlagShowPullLogs = "show-plogs"
|
||||
|
||||
//Compose-related flags
|
||||
FlagComposeFile = "compose-file"
|
||||
FlagTargetComposeSvc = "target-compose-svc"
|
||||
FlagDepIncludeComposeSvc = "dep-include-compose-svc"
|
||||
FlagDepExcludeComposeSvc = "dep-exclude-compose-svc"
|
||||
FlagDepIncludeComposeSvcDeps = "dep-include-compose-svc-deps"
|
||||
FlagComposeNet = "compose-net"
|
||||
|
||||
FlagRemoveFileArtifacts = "remove-file-artifacts"
|
||||
FlagCopyMetaArtifacts = "copy-meta-artifacts"
|
||||
|
||||
@ -119,6 +127,14 @@ const (
|
||||
FlagPullUsage = "Try pulling target if it's not available locally"
|
||||
FlagShowPullLogsUsage = "Show image pull logs"
|
||||
|
||||
//Compose-related flags
|
||||
FlagComposeFileUsage = "Load container info from selected compose file"
|
||||
FlagTargetComposeSvcUsage = "Target service from compose file"
|
||||
FlagDepIncludeComposeSvcUsage = "Include specific compose service as a target dependency (only selected services will be started)"
|
||||
FlagDepExcludeComposeSvcUsage = "Exclude specific service from the compose services that will be started as target dependencies"
|
||||
FlagDepIncludeComposeSvcDepsUsage = "Include all dependencies for the selected compose service (excluding the service itself) as target dependencies"
|
||||
FlagComposeNetUsage = "Attach target to the selected compose network(s) otherwise all networks will be attached"
|
||||
|
||||
FlagRemoveFileArtifactsUsage = "remove file artifacts when command is done"
|
||||
FlagCopyMetaArtifactsUsage = "copy metadata artifacts to the selected location when command is done"
|
||||
|
||||
@ -270,6 +286,42 @@ var CommonFlags = map[string]cli.Flag{
|
||||
Usage: FlagShowPullLogsUsage,
|
||||
EnvVar: "DSLIM_PLOG",
|
||||
},
|
||||
FlagComposeFile: cli.StringFlag{
|
||||
Name: FlagComposeFile,
|
||||
Value: "",
|
||||
Usage: FlagComposeFileUsage,
|
||||
EnvVar: "DSLIM_COMPOSE_FILE",
|
||||
},
|
||||
FlagTargetComposeSvc: cli.StringFlag{
|
||||
Name: FlagTargetComposeSvc,
|
||||
Value: "",
|
||||
Usage: FlagTargetComposeSvcUsage,
|
||||
EnvVar: "DSLIM_TARGET_COMPOSE_SVC",
|
||||
},
|
||||
FlagDepIncludeComposeSvcDeps: cli.StringFlag{
|
||||
Name: FlagDepIncludeComposeSvcDeps,
|
||||
Value: "",
|
||||
Usage: FlagDepIncludeComposeSvcDepsUsage,
|
||||
EnvVar: "DSLIM_DEP_INCLUDE_COMPOSE_SVC_DEPS",
|
||||
},
|
||||
FlagDepIncludeComposeSvc: cli.StringSliceFlag{
|
||||
Name: FlagDepIncludeComposeSvc,
|
||||
Value: &cli.StringSlice{},
|
||||
Usage: FlagDepIncludeComposeSvcUsage,
|
||||
EnvVar: "DSLIM_DEP_INCLUDE_COMPOSE_SVC",
|
||||
},
|
||||
FlagDepExcludeComposeSvc: cli.StringSliceFlag{
|
||||
Name: FlagDepExcludeComposeSvc,
|
||||
Value: &cli.StringSlice{},
|
||||
Usage: FlagDepExcludeComposeSvcUsage,
|
||||
EnvVar: "DSLIM_DEP_EXCLUDE_COMPOSE_SVC",
|
||||
},
|
||||
FlagComposeNet: cli.StringSliceFlag{
|
||||
Name: FlagComposeNet,
|
||||
Value: &cli.StringSlice{},
|
||||
Usage: FlagComposeNetUsage,
|
||||
EnvVar: "DSLIM_COMPOSE_NET",
|
||||
},
|
||||
FlagRemoveFileArtifacts: cli.BoolFlag{
|
||||
Name: FlagRemoveFileArtifacts,
|
||||
Usage: FlagRemoveFileArtifactsUsage,
|
||||
|
1640
pkg/app/master/compose/execution.go
Normal file
1640
pkg/app/master/compose/execution.go
Normal file
File diff suppressed because it is too large
Load Diff
2
pkg/third_party/compose-go/.github/CODEOWNERS
vendored
Executable file
2
pkg/third_party/compose-go/.github/CODEOWNERS
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
* @ndeloof
|
||||
|
15
pkg/third_party/compose-go/.github/dependabot.yml
vendored
Executable file
15
pkg/third_party/compose-go/.github/dependabot.yml
vendored
Executable file
@ -0,0 +1,15 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: github.com/sirupsen/logrus
|
||||
versions:
|
||||
- 1.8.0
|
||||
- 1.8.1
|
||||
- dependency-name: github.com/google/go-cmp
|
||||
versions:
|
||||
- 0.5.5
|
31
pkg/third_party/compose-go/.github/workflows/ci.yml
vendored
Executable file
31
pkg/third_party/compose-go/.github/workflows/ci.yml
vendored
Executable file
@ -0,0 +1,31 @@
|
||||
on: [push, pull_request]
|
||||
name: Continuous integration
|
||||
jobs:
|
||||
validate:
|
||||
name: validate
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Lint code
|
||||
run: DOCKER_BUILDKIT=1 make lint
|
||||
- name: Check license
|
||||
run: DOCKER_BUILDKIT=1 make check-license
|
||||
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x]
|
||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Test
|
||||
run: go test ./...
|
13
pkg/third_party/compose-go/.github/workflows/release.yml
vendored
Executable file
13
pkg/third_party/compose-go/.github/workflows/release.yml
vendored
Executable file
@ -0,0 +1,13 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v1*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
2
pkg/third_party/compose-go/.gitignore
vendored
Executable file
2
pkg/third_party/compose-go/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
### IDEs ###
|
||||
.idea/*
|
16
pkg/third_party/compose-go/.pre-commit-config.yaml
vendored
Executable file
16
pkg/third_party/compose-go/.pre-commit-config.yaml
vendored
Executable file
@ -0,0 +1,16 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
- id: check-yaml
|
||||
exclude: '^vendor/'
|
||||
- id: end-of-file-fixer
|
||||
exclude: '^vendor/'
|
||||
- id: trailing-whitespace
|
||||
exclude: '^vendor/'
|
||||
- repo: https://github.com/dnephin/pre-commit-golang
|
||||
rev: v0.3.5
|
||||
hooks:
|
||||
- id: go-fmt
|
||||
- id: golangci-lint
|
||||
- id: go-imports
|
127
pkg/third_party/compose-go/CODE_OF_CONDUCT.md
vendored
Executable file
127
pkg/third_party/compose-go/CODE_OF_CONDUCT.md
vendored
Executable file
@ -0,0 +1,127 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
88
pkg/third_party/compose-go/CONTRIBUTING.md
vendored
Executable file
88
pkg/third_party/compose-go/CONTRIBUTING.md
vendored
Executable file
@ -0,0 +1,88 @@
|
||||
# Contributing
|
||||
|
||||
Contributions should be made via pull requests. Pull requests will be reviewed
|
||||
by one or more maintainers and merged when acceptable.
|
||||
|
||||
The goal of the Compose Go library is to facilitate parsing and loading Compose
|
||||
files.
|
||||
|
||||
## Commit Messages
|
||||
|
||||
Commit messages should follow best practices and explain the context of the
|
||||
problem and how it was solved– including any caveats or follow up changes
|
||||
required. They should tell the story of the change and provide readers an
|
||||
understanding of what led to it.
|
||||
|
||||
[How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/)
|
||||
provides a good guide for how to do so.
|
||||
|
||||
In practice, the best approach to maintaining a nice commit message is to
|
||||
leverage a `git add -p` and `git commit --amend` to formulate a solid
|
||||
change set. This allows one to piece together a change, as information becomes
|
||||
available.
|
||||
|
||||
If you squash a series of commits, don't just submit that. Re-write the commit
|
||||
message, as if the series of commits was a single stroke of brilliance.
|
||||
|
||||
That said, there is no requirement to have a single commit for a pull request,
|
||||
as long as each commit tells the story. For example, if there is a feature that
|
||||
requires a package, it might make sense to have the package in a separate commit
|
||||
then have a subsequent commit that uses it.
|
||||
|
||||
Remember, you're telling part of the story with the commit message. Don't make
|
||||
your chapter weird.
|
||||
|
||||
## Sign your work
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for the patch. Your
|
||||
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||
it on as an open-source patch. The rules are pretty simple: if you can certify
|
||||
the below (from [developercertificate.org](http://developercertificate.org/)):
|
||||
|
||||
```
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
```
|
||||
|
||||
Then you just add a line to every git commit message:
|
||||
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
|
||||
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
|
||||
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||
commit automatically with `git commit -s`.
|
||||
|
2
vendor/github.com/containerd/continuity/LICENSE → pkg/third_party/compose-go/LICENSE
vendored
Normal file → Executable file
2
vendor/github.com/containerd/continuity/LICENSE → pkg/third_party/compose-go/LICENSE
vendored
Normal file → Executable file
@ -176,7 +176,7 @@
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright The containerd Authors
|
||||
Copyright 2013-2017 Docker, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
9
pkg/third_party/compose-go/MAINTENANCE.md
vendored
Executable file
9
pkg/third_party/compose-go/MAINTENANCE.md
vendored
Executable file
@ -0,0 +1,9 @@
|
||||
# Maintenance
|
||||
|
||||
The compose-go library has to be kept up-to-date with approved changes in the [Compose specification](https://github.com/compose-spec/compose-spec).
|
||||
As we define new attributes to be added to the spec, this typically requires:
|
||||
|
||||
1. Updating `schema` to latest version from compose-spec
|
||||
1. Creating the matching struct/field in `types`
|
||||
1. Creating the matching `CheckXX` method in `compatibility`
|
||||
1. If the new attribute replaces a legacy one we want to deprecate, creating the adequate logic in `normalize.go`
|
44
pkg/third_party/compose-go/Makefile
vendored
Executable file
44
pkg/third_party/compose-go/Makefile
vendored
Executable file
@ -0,0 +1,44 @@
|
||||
# Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
IMAGE_PREFIX=composespec/conformance-tests-
|
||||
|
||||
.PHONY: build
|
||||
build: ## Run tests
|
||||
go build ./...
|
||||
|
||||
.PHONY: test
|
||||
test: ## Run tests
|
||||
gotestsum ./...
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: ## Format go files
|
||||
go fmt ./...
|
||||
|
||||
.PHONY: build-validate-image
|
||||
build-validate-image:
|
||||
docker build . -f ci/Dockerfile -t $(IMAGE_PREFIX)validate
|
||||
|
||||
.PHONY: lint
|
||||
lint: build-validate-image
|
||||
docker run --rm $(IMAGE_PREFIX)validate bash -c "golangci-lint run --config ./golangci.yml ./..."
|
||||
|
||||
.PHONY: check-license
|
||||
check-license: build-validate-image
|
||||
docker run --rm $(IMAGE_PREFIX)validate bash -c "./scripts/validate/fileheader"
|
||||
|
||||
.PHONY: setup
|
||||
setup: ## Setup the precommit hook
|
||||
@which pre-commit > /dev/null 2>&1 || (echo "pre-commit not installed see README." && false)
|
||||
@pre-commit install
|
2
pkg/third_party/compose-go/NOTICE
vendored
Executable file
2
pkg/third_party/compose-go/NOTICE
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
The Compose Specification
|
||||
Copyright 2020 The Compose Specification Authors
|
11
pkg/third_party/compose-go/README.md
vendored
Executable file
11
pkg/third_party/compose-go/README.md
vendored
Executable file
@ -0,0 +1,11 @@
|
||||
# compose-go
|
||||

|
||||
|
||||
Go reference library for parsing and loading Compose files as specified by the
|
||||
[Compose specification](https://github.com/compose-spec/compose-spec).
|
||||
|
||||
## Used by
|
||||
|
||||
* [compose-ref](https://github.com/compose-spec/compose-ref)
|
||||
* [containerd/nerdctl](https://github.com/containerd/nerdctl)
|
||||
* [compose-cli](https://github.com/docker/compose-cli)
|
23
pkg/third_party/compose-go/ci/Dockerfile
vendored
Executable file
23
pkg/third_party/compose-go/ci/Dockerfile
vendored
Executable file
@ -0,0 +1,23 @@
|
||||
# Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM golang:1.16
|
||||
|
||||
WORKDIR /go/src
|
||||
|
||||
ARG GOLANGCILINT_VERSION=v1.24.0
|
||||
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCILINT_VERSION}
|
||||
RUN go get -v -u github.com/kunalkushwaha/ltag && rm -rf /go/src/github.com/kunalkushwaha
|
||||
|
||||
COPY . .
|
411
pkg/third_party/compose-go/cli/options.go
vendored
Executable file
411
pkg/third_party/compose-go/cli/options.go
vendored
Executable file
@ -0,0 +1,411 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/errdefs"
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/ulyssessouza/godotenv"
|
||||
)
|
||||
|
||||
// ProjectOptions groups the command line options recommended for a Compose implementation
|
||||
type ProjectOptions struct {
|
||||
Name string
|
||||
WorkingDir string
|
||||
ConfigPaths []string
|
||||
Environment map[string]string
|
||||
EnvFile string
|
||||
loadOptions []func(*loader.Options)
|
||||
}
|
||||
|
||||
type ProjectOptionsFn func(*ProjectOptions) error
|
||||
|
||||
// NewProjectOptions creates ProjectOptions
|
||||
func NewProjectOptions(configs []string, opts ...ProjectOptionsFn) (*ProjectOptions, error) {
|
||||
options := &ProjectOptions{
|
||||
ConfigPaths: configs,
|
||||
Environment: map[string]string{},
|
||||
}
|
||||
for _, o := range opts {
|
||||
err := o(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
// WithName defines ProjectOptions' name
|
||||
func WithName(name string) ProjectOptionsFn {
|
||||
return func(o *ProjectOptions) error {
|
||||
o.Name = name
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithWorkingDirectory defines ProjectOptions' working directory
|
||||
func WithWorkingDirectory(wd string) ProjectOptionsFn {
|
||||
return func(o *ProjectOptions) error {
|
||||
if wd == "" {
|
||||
return nil
|
||||
}
|
||||
abs, err := filepath.Abs(wd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.WorkingDir = abs
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithConfigFileEnv allow to set compose config file paths by COMPOSE_FILE environment variable
|
||||
func WithConfigFileEnv(o *ProjectOptions) error {
|
||||
if len(o.ConfigPaths) > 0 {
|
||||
return nil
|
||||
}
|
||||
sep := o.Environment[ComposePathSeparator]
|
||||
if sep == "" {
|
||||
sep = string(os.PathListSeparator)
|
||||
}
|
||||
f, ok := o.Environment[ComposeFilePath]
|
||||
if ok {
|
||||
paths, err := absolutePaths(strings.Split(f, sep))
|
||||
o.ConfigPaths = paths
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithDefaultConfigPath searches for default config files from working directory
|
||||
func WithDefaultConfigPath(o *ProjectOptions) error {
|
||||
if len(o.ConfigPaths) > 0 {
|
||||
return nil
|
||||
}
|
||||
pwd, err := o.GetWorkingDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
candidates := findFiles(DefaultFileNames, pwd)
|
||||
if len(candidates) > 0 {
|
||||
winner := candidates[0]
|
||||
if len(candidates) > 1 {
|
||||
logrus.Warnf("Found multiple config files with supported names: %s", strings.Join(candidates, ", "))
|
||||
logrus.Warnf("Using %s", winner)
|
||||
}
|
||||
o.ConfigPaths = append(o.ConfigPaths, winner)
|
||||
|
||||
overrides := findFiles(DefaultOverrideFileNames, pwd)
|
||||
if len(overrides) > 0 {
|
||||
if len(overrides) > 1 {
|
||||
logrus.Warnf("Found multiple override files with supported names: %s", strings.Join(overrides, ", "))
|
||||
logrus.Warnf("Using %s", overrides[0])
|
||||
}
|
||||
o.ConfigPaths = append(o.ConfigPaths, overrides[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
parent := filepath.Dir(pwd)
|
||||
if parent == pwd {
|
||||
return errors.Wrap(errdefs.ErrNotFound, "can't find a suitable configuration file in this directory or any parent")
|
||||
}
|
||||
pwd = parent
|
||||
}
|
||||
}
|
||||
|
||||
// WithEnv defines a key=value set of variables used for compose file interpolation
|
||||
func WithEnv(env []string) ProjectOptionsFn {
|
||||
return func(o *ProjectOptions) error {
|
||||
for k, v := range getAsEqualsMap(env) {
|
||||
o.Environment[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDiscardEnvFiles sets discards the `env_file` section after resolving to
|
||||
// the `environment` section
|
||||
func WithDiscardEnvFile(o *ProjectOptions) error {
|
||||
o.loadOptions = append(o.loadOptions, loader.WithDiscardEnvFiles)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithLoadOptions provides a hook to control how compose files are loaded
|
||||
func WithLoadOptions(loadOptions ...func(*loader.Options)) ProjectOptionsFn {
|
||||
return func(o *ProjectOptions) error {
|
||||
o.loadOptions = append(o.loadOptions, loadOptions...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithOsEnv imports environment variables from OS
|
||||
func WithOsEnv(o *ProjectOptions) error {
|
||||
for k, v := range getAsEqualsMap(os.Environ()) {
|
||||
o.Environment[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithEnvFile set an alternate env file
|
||||
func WithEnvFile(file string) ProjectOptionsFn {
|
||||
return func(options *ProjectOptions) error {
|
||||
options.EnvFile = file
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDotEnv imports environment variables from .env file
|
||||
func WithDotEnv(o *ProjectOptions) error {
|
||||
dotEnvFile := o.EnvFile
|
||||
if dotEnvFile == "" {
|
||||
wd, err := o.GetWorkingDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dotEnvFile = filepath.Join(wd, ".env")
|
||||
}
|
||||
abs, err := filepath.Abs(dotEnvFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dotEnvFile = abs
|
||||
|
||||
s, err := os.Stat(dotEnvFile)
|
||||
if os.IsNotExist(err) {
|
||||
if o.EnvFile != "" {
|
||||
return errors.Errorf("Couldn't find env file: %s", o.EnvFile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.IsDir() {
|
||||
if o.EnvFile != "" {
|
||||
return errors.Errorf("%s is a directory", dotEnvFile)
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Open(dotEnvFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
notInEnvSet := make(map[string]interface{})
|
||||
env, err := godotenv.ParseWithLookup(file, func(k string) (string, bool) {
|
||||
v, ok := os.LookupEnv(k)
|
||||
if !ok {
|
||||
notInEnvSet[k] = nil
|
||||
return "", true
|
||||
}
|
||||
return v, true
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range env {
|
||||
if _, ok := notInEnvSet[k]; ok {
|
||||
continue
|
||||
}
|
||||
o.Environment[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithInterpolation set ProjectOptions to enable/skip interpolation
|
||||
func WithInterpolation(interpolation bool) ProjectOptionsFn {
|
||||
return func(o *ProjectOptions) error {
|
||||
o.loadOptions = append(o.loadOptions, func(options *loader.Options) {
|
||||
options.SkipInterpolation = !interpolation
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithResolvedPaths set ProjectOptions to enable paths resolution
|
||||
func WithResolvedPaths(resolve bool) ProjectOptionsFn {
|
||||
return func(o *ProjectOptions) error {
|
||||
o.loadOptions = append(o.loadOptions, func(options *loader.Options) {
|
||||
options.ResolvePaths = resolve
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultFileNames defines the Compose file names for auto-discovery (in order of preference)
|
||||
var DefaultFileNames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"}
|
||||
|
||||
// DefaultOverrideFileNames defines the Compose override file names for auto-discovery (in order of preference)
|
||||
var DefaultOverrideFileNames = []string{"compose.override.yml", "compose.override.yaml", "docker-compose.override.yml", "docker-compose.override.yaml"}
|
||||
|
||||
const (
|
||||
ComposeProjectName = "COMPOSE_PROJECT_NAME"
|
||||
ComposePathSeparator = "COMPOSE_PATH_SEPARATOR"
|
||||
ComposeFilePath = "COMPOSE_FILE"
|
||||
)
|
||||
|
||||
func (o ProjectOptions) GetWorkingDir() (string, error) {
|
||||
if o.WorkingDir != "" {
|
||||
return o.WorkingDir, nil
|
||||
}
|
||||
for _, path := range o.ConfigPaths {
|
||||
if path != "-" {
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Dir(absPath), nil
|
||||
}
|
||||
}
|
||||
return os.Getwd()
|
||||
}
|
||||
|
||||
// ProjectFromOptions load a compose project based on command line options
|
||||
func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
|
||||
configPaths, err := getConfigPathsFromOptions(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var configs []types.ConfigFile
|
||||
for _, f := range configPaths {
|
||||
var b []byte
|
||||
if f == "-" {
|
||||
b, err = ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
f, err := filepath.Abs(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err = ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
configs = append(configs, types.ConfigFile{
|
||||
Filename: f,
|
||||
Content: b,
|
||||
})
|
||||
}
|
||||
|
||||
workingDir, err := options.GetWorkingDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
absWorkingDir, err := filepath.Abs(workingDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nameLoadOpt = func(opts *loader.Options) {
|
||||
if options.Name != "" {
|
||||
opts.Name = options.Name
|
||||
} else if nameFromEnv, ok := options.Environment[ComposeProjectName]; ok && nameFromEnv != "" {
|
||||
opts.Name = nameFromEnv
|
||||
} else {
|
||||
opts.Name = regexp.MustCompile(`(?m)[a-z]+[-_a-z0-9]*`).FindString(strings.ToLower(filepath.Base(absWorkingDir)))
|
||||
}
|
||||
opts.Name = strings.ToLower(opts.Name)
|
||||
}
|
||||
options.loadOptions = append(options.loadOptions, nameLoadOpt)
|
||||
|
||||
project, err := loader.Load(types.ConfigDetails{
|
||||
ConfigFiles: configs,
|
||||
WorkingDir: workingDir,
|
||||
Environment: options.Environment,
|
||||
}, options.loadOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
project.ComposeFiles = configPaths
|
||||
return project, nil
|
||||
}
|
||||
|
||||
// getConfigPathsFromOptions retrieves the config files for project based on project options
|
||||
func getConfigPathsFromOptions(options *ProjectOptions) ([]string, error) {
|
||||
if len(options.ConfigPaths) != 0 {
|
||||
return absolutePaths(options.ConfigPaths)
|
||||
}
|
||||
|
||||
return nil, errors.New("no configuration file provided")
|
||||
}
|
||||
|
||||
func findFiles(names []string, pwd string) []string {
|
||||
candidates := []string{}
|
||||
for _, n := range names {
|
||||
f := filepath.Join(pwd, n)
|
||||
if _, err := os.Stat(f); err == nil {
|
||||
candidates = append(candidates, f)
|
||||
}
|
||||
}
|
||||
return candidates
|
||||
}
|
||||
|
||||
func absolutePaths(p []string) ([]string, error) {
|
||||
var paths []string
|
||||
for _, f := range p {
|
||||
if f == "-" {
|
||||
paths = append(paths, f)
|
||||
continue
|
||||
}
|
||||
abs, err := filepath.Abs(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f = abs
|
||||
if _, err := os.Stat(f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, f)
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
// getAsEqualsMap split key=value formatted strings into a key : value map
|
||||
func getAsEqualsMap(em []string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, v := range em {
|
||||
kv := strings.SplitN(v, "=", 2)
|
||||
m[kv[0]] = kv[1]
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// getAsEqualsMap format a key : value map into key=value strings
|
||||
func getAsStringList(em map[string]string) []string {
|
||||
m := make([]string, 0, len(em))
|
||||
for k, v := range em {
|
||||
m = append(m, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
return m
|
||||
}
|
168
pkg/third_party/compose-go/cli/options_test.go
vendored
Executable file
168
pkg/third_party/compose-go/cli/options_test.go
vendored
Executable file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestProjectName(t *testing.T) {
|
||||
t.Run("by name", func(t *testing.T) {
|
||||
opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithName("my_project"))
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, p.Name, "my_project")
|
||||
})
|
||||
|
||||
t.Run("by working dir", func(t *testing.T) {
|
||||
opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithWorkingDirectory("."))
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, p.Name, "cli")
|
||||
})
|
||||
|
||||
t.Run("by compose file parent dir", func(t *testing.T) {
|
||||
opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"})
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, p.Name, "simple")
|
||||
})
|
||||
|
||||
t.Run("by COMPOSE_PROJECT_NAME", func(t *testing.T) {
|
||||
os.Setenv("COMPOSE_PROJECT_NAME", "my_project_from_env")
|
||||
defer os.Unsetenv("COMPOSE_PROJECT_NAME")
|
||||
opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithOsEnv)
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, p.Name, "my_project_from_env")
|
||||
})
|
||||
|
||||
t.Run("by .env", func(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
assert.NilError(t, err)
|
||||
err = os.Chdir("testdata/env-file")
|
||||
assert.NilError(t, err)
|
||||
defer os.Chdir(wd)
|
||||
|
||||
opts, err := NewProjectOptions(nil, WithDotEnv, WithConfigFileEnv)
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, p.Name, "my_project_from_dot_env")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestProjectFromSetOfFiles(t *testing.T) {
|
||||
opts, err := NewProjectOptions([]string{
|
||||
"testdata/simple/compose.yaml",
|
||||
"testdata/simple/compose-with-overrides.yaml",
|
||||
}, WithName("my_project"))
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
service, err := p.GetService("simple")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, service.Image, "haproxy")
|
||||
}
|
||||
|
||||
func TestProjectComposefilesFromSetOfFiles(t *testing.T) {
|
||||
opts, err := NewProjectOptions([]string{},
|
||||
WithWorkingDirectory("testdata/simple/"),
|
||||
WithName("my_project"),
|
||||
WithDefaultConfigPath,
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
absPath, _ := filepath.Abs(filepath.Join("testdata", "simple", "compose.yaml"))
|
||||
assert.DeepEqual(t, p.ComposeFiles, []string{absPath})
|
||||
}
|
||||
|
||||
func TestProjectComposefilesFromWorkingDir(t *testing.T) {
|
||||
opts, err := NewProjectOptions([]string{
|
||||
"testdata/simple/compose.yaml",
|
||||
"testdata/simple/compose-with-overrides.yaml",
|
||||
}, WithName("my_project"))
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
currentDir, _ := os.Getwd()
|
||||
assert.DeepEqual(t, p.ComposeFiles, []string{
|
||||
filepath.Join(currentDir, "testdata/simple/compose.yaml"),
|
||||
filepath.Join(currentDir, "testdata/simple/compose-with-overrides.yaml"),
|
||||
})
|
||||
}
|
||||
|
||||
func TestProjectWithDotEnv(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
assert.NilError(t, err)
|
||||
err = os.Chdir("testdata/simple")
|
||||
assert.NilError(t, err)
|
||||
defer os.Chdir(wd)
|
||||
|
||||
opts, err := NewProjectOptions([]string{
|
||||
"compose-with-variables.yaml",
|
||||
}, WithName("my_project"), WithDotEnv)
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
service, err := p.GetService("simple")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, service.Ports[0].Published, uint32(8000))
|
||||
}
|
||||
|
||||
func TestProjectWithDiscardEnvFile(t *testing.T) {
|
||||
opts, err := NewProjectOptions([]string{
|
||||
"testdata/env-file/compose-with-env-file.yaml",
|
||||
}, WithDiscardEnvFile)
|
||||
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
service, err := p.GetService("simple")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, *service.Environment["DEFAULT_PORT"], "8080")
|
||||
assert.Assert(t, service.EnvFile == nil)
|
||||
}
|
||||
|
||||
func TestProjectNameFromWorkingDir(t *testing.T) {
|
||||
opts, err := NewProjectOptions([]string{
|
||||
"testdata/env-file/compose-with-env-file.yaml",
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
p, err := ProjectFromOptions(opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, p.Name, "env-file")
|
||||
}
|
||||
|
||||
func TestEnvMap(t *testing.T) {
|
||||
m := map[string]string{}
|
||||
m["foo"] = "bar"
|
||||
l := getAsStringList(m)
|
||||
assert.Equal(t, l[0], "foo=bar")
|
||||
m = getAsEqualsMap(l)
|
||||
assert.Equal(t, m["foo"], "bar")
|
||||
}
|
2
pkg/third_party/compose-go/cli/testdata/env-file/.env
vendored
Executable file
2
pkg/third_party/compose-go/cli/testdata/env-file/.env
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
COMPOSE_FILE=compose-with-env-file.yaml
|
||||
COMPOSE_PROJECT_NAME=my_project_from_dot_env
|
8
pkg/third_party/compose-go/cli/testdata/env-file/compose-with-env-file.yaml
vendored
Executable file
8
pkg/third_party/compose-go/cli/testdata/env-file/compose-with-env-file.yaml
vendored
Executable file
@ -0,0 +1,8 @@
|
||||
version: "3"
|
||||
services:
|
||||
simple:
|
||||
image: nginx
|
||||
env_file:
|
||||
- ./simple-env
|
||||
ports:
|
||||
- 8000:80
|
1
pkg/third_party/compose-go/cli/testdata/env-file/simple-env
vendored
Executable file
1
pkg/third_party/compose-go/cli/testdata/env-file/simple-env
vendored
Executable file
@ -0,0 +1 @@
|
||||
DEFAULT_PORT=8080
|
1
pkg/third_party/compose-go/cli/testdata/simple/.env
vendored
Executable file
1
pkg/third_party/compose-go/cli/testdata/simple/.env
vendored
Executable file
@ -0,0 +1 @@
|
||||
PUBLIC_PORT=8000
|
3
pkg/third_party/compose-go/cli/testdata/simple/compose-with-overrides.yaml
vendored
Executable file
3
pkg/third_party/compose-go/cli/testdata/simple/compose-with-overrides.yaml
vendored
Executable file
@ -0,0 +1,3 @@
|
||||
services:
|
||||
simple:
|
||||
image: haproxy
|
5
pkg/third_party/compose-go/cli/testdata/simple/compose-with-variables.yaml
vendored
Executable file
5
pkg/third_party/compose-go/cli/testdata/simple/compose-with-variables.yaml
vendored
Executable file
@ -0,0 +1,5 @@
|
||||
services:
|
||||
simple:
|
||||
image: nginx
|
||||
ports:
|
||||
- ${PUBLIC_PORT}:80
|
3
pkg/third_party/compose-go/cli/testdata/simple/compose.yaml
vendored
Executable file
3
pkg/third_party/compose-go/cli/testdata/simple/compose.yaml
vendored
Executable file
@ -0,0 +1,3 @@
|
||||
services:
|
||||
simple:
|
||||
image: nginx
|
54
pkg/third_party/compose-go/compatibility/allowlist.go
vendored
Executable file
54
pkg/third_party/compose-go/compatibility/allowlist.go
vendored
Executable file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/compose-spec/compose-go/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// AllowList implements the Checker interface by rejecting all attributes that are not listed as "supported".
|
||||
type AllowList struct {
|
||||
Supported []string
|
||||
errors []error
|
||||
}
|
||||
|
||||
// Errors returns the list of errors encountered when checking against the allow list
|
||||
func (c *AllowList) Errors() []error {
|
||||
return c.errors
|
||||
}
|
||||
|
||||
func (c *AllowList) supported(attributes ...string) bool {
|
||||
for _, a := range attributes {
|
||||
for _, s := range c.Supported {
|
||||
if s == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *AllowList) Unsupported(message string, args ...interface{}) {
|
||||
c.errors = append(c.errors, errors.Wrap(errdefs.ErrUnsupported, fmt.Sprintf(message, args...)))
|
||||
}
|
||||
|
||||
func (c *AllowList) Incompatible(message string, args ...interface{}) {
|
||||
c.errors = append(c.errors, errors.Wrap(errdefs.ErrIncompatible, fmt.Sprintf(message, args...)))
|
||||
}
|
78
pkg/third_party/compose-go/compatibility/allowlist_test.go
vendored
Executable file
78
pkg/third_party/compose-go/compatibility/allowlist_test.go
vendored
Executable file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/errdefs"
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestAllowList(t *testing.T) {
|
||||
var checker Checker = customChecker{
|
||||
&AllowList{
|
||||
Supported: []string{
|
||||
"services.image",
|
||||
"services.network_mode",
|
||||
"services.privileged",
|
||||
"services.networks",
|
||||
"services.scale",
|
||||
},
|
||||
},
|
||||
}
|
||||
dict := []byte(`
|
||||
services:
|
||||
foo:
|
||||
image: busybox
|
||||
network_mode: host
|
||||
privileged: true
|
||||
mac_address: "a:b:c:d"
|
||||
`)
|
||||
|
||||
project, err := loader.Load(types.ConfigDetails{
|
||||
ConfigFiles: []types.ConfigFile{
|
||||
{Filename: "filename.yml", Content: dict},
|
||||
},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
|
||||
Check(project, checker)
|
||||
errors := checker.Errors()
|
||||
assert.Check(t, len(errors) == 2)
|
||||
assert.Check(t, errdefs.IsUnsupportedError(errors[0]))
|
||||
assert.Equal(t, errors[0].Error(), "services.mac_address: unsupported attribute")
|
||||
|
||||
assert.Check(t, errdefs.IsUnsupportedError(errors[1]))
|
||||
assert.Equal(t, errors[1].Error(), "services.network_mode=host: unsupported attribute")
|
||||
|
||||
service, err := project.GetService("foo")
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, service.MacAddress == "")
|
||||
}
|
||||
|
||||
type customChecker struct {
|
||||
*AllowList
|
||||
}
|
||||
|
||||
func (c customChecker) CheckNetworkMode(service *types.ServiceConfig) {
|
||||
if service.NetworkMode == "host" {
|
||||
c.Unsupported("services.network_mode=host")
|
||||
}
|
||||
}
|
77
pkg/third_party/compose-go/compatibility/build.go
vendored
Executable file
77
pkg/third_party/compose-go/compatibility/build.go
vendored
Executable file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compatibility
|
||||
|
||||
import "github.com/compose-spec/compose-go/types"
|
||||
|
||||
func (c *AllowList) CheckBuild(service *types.ServiceConfig) bool {
|
||||
if !c.supported("services.build") && service.Build != nil {
|
||||
service.Build = nil
|
||||
c.Unsupported("services.build")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBuildArgs(build *types.BuildConfig) {
|
||||
if !c.supported("services.build.args") && len(build.Args) != 0 {
|
||||
build.Args = nil
|
||||
c.Unsupported("services.build.args")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBuildLabels(build *types.BuildConfig) {
|
||||
if !c.supported("services.build.labels") && len(build.Labels) != 0 {
|
||||
build.Labels = nil
|
||||
c.Unsupported("services.build.labels")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBuildCacheFrom(build *types.BuildConfig) {
|
||||
if !c.supported("services.build.cache_from") && len(build.CacheFrom) != 0 {
|
||||
build.CacheFrom = nil
|
||||
c.Unsupported("services.build.cache_from")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBuildExtraHosts(build *types.BuildConfig) {
|
||||
if !c.supported("services.build.extra_hosts") && len(build.ExtraHosts) != 0 {
|
||||
build.ExtraHosts = nil
|
||||
c.Unsupported("services.build.extra_hosts")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBuildIsolation(build *types.BuildConfig) {
|
||||
if !c.supported("services.build.isolation") && build.Isolation != "" {
|
||||
build.Isolation = ""
|
||||
c.Unsupported("services.build.isolation")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBuildNetwork(build *types.BuildConfig) {
|
||||
if !c.supported("services.build.network") && build.Network != "" {
|
||||
build.Network = ""
|
||||
c.Unsupported("services.build.network")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBuildTarget(build *types.BuildConfig) {
|
||||
if !c.supported("services.build.target") && build.Target != "" {
|
||||
build.Target = ""
|
||||
c.Unsupported("services.build.target")
|
||||
}
|
||||
}
|
437
pkg/third_party/compose-go/compatibility/checker.go
vendored
Executable file
437
pkg/third_party/compose-go/compatibility/checker.go
vendored
Executable file
@ -0,0 +1,437 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"github.com/compose-spec/compose-go/errdefs"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
type Checker interface {
|
||||
Errors() []error
|
||||
CheckBlkioConfig(build *types.ServiceConfig)
|
||||
CheckBlkioWeight(build *types.BlkioConfig)
|
||||
CheckBlkioWeightDevice(build *types.BlkioConfig)
|
||||
CheckBlkioDeviceReadBps(build *types.BlkioConfig)
|
||||
CheckBlkioDeviceReadIOps(build *types.BlkioConfig)
|
||||
CheckBlkioDeviceWriteBps(build *types.BlkioConfig)
|
||||
CheckBlkioDeviceWriteIOps(build *types.BlkioConfig)
|
||||
CheckBuild(build *types.ServiceConfig) bool
|
||||
CheckBuildArgs(build *types.BuildConfig)
|
||||
CheckBuildLabels(build *types.BuildConfig)
|
||||
CheckBuildCacheFrom(build *types.BuildConfig)
|
||||
CheckBuildExtraHosts(build *types.BuildConfig)
|
||||
CheckBuildIsolation(build *types.BuildConfig)
|
||||
CheckBuildNetwork(build *types.BuildConfig)
|
||||
CheckBuildTarget(build *types.BuildConfig)
|
||||
CheckCapAdd(service *types.ServiceConfig)
|
||||
CheckCapDrop(service *types.ServiceConfig)
|
||||
CheckCgroupParent(service *types.ServiceConfig)
|
||||
CheckCPUCount(service *types.ServiceConfig)
|
||||
CheckCPUPercent(service *types.ServiceConfig)
|
||||
CheckCPUPeriod(service *types.ServiceConfig)
|
||||
CheckCPUQuota(service *types.ServiceConfig)
|
||||
CheckCPURTRuntime(service *types.ServiceConfig)
|
||||
CheckCPURTPeriod(service *types.ServiceConfig)
|
||||
CheckCPUs(service *types.ServiceConfig)
|
||||
CheckCPUSet(service *types.ServiceConfig)
|
||||
CheckCPUShares(service *types.ServiceConfig)
|
||||
CheckCommand(service *types.ServiceConfig)
|
||||
CheckConfigs(service *types.ServiceConfig)
|
||||
CheckContainerName(service *types.ServiceConfig)
|
||||
CheckCredentialSpec(service *types.ServiceConfig)
|
||||
CheckDependsOn(service *types.ServiceConfig)
|
||||
CheckDevices(service *types.ServiceConfig)
|
||||
CheckDNS(service *types.ServiceConfig)
|
||||
CheckDNSOpts(service *types.ServiceConfig)
|
||||
CheckDNSSearch(service *types.ServiceConfig)
|
||||
CheckDomainName(service *types.ServiceConfig)
|
||||
CheckEntrypoint(service *types.ServiceConfig)
|
||||
CheckEnvironment(service *types.ServiceConfig)
|
||||
CheckEnvFile(service *types.ServiceConfig)
|
||||
CheckExpose(service *types.ServiceConfig)
|
||||
CheckExtends(service *types.ServiceConfig)
|
||||
CheckExternalLinks(service *types.ServiceConfig)
|
||||
CheckExtraHosts(service *types.ServiceConfig)
|
||||
CheckGroupAdd(service *types.ServiceConfig)
|
||||
CheckHostname(service *types.ServiceConfig)
|
||||
CheckHealthCheckTest(h *types.HealthCheckConfig)
|
||||
CheckHealthCheckTimeout(h *types.HealthCheckConfig)
|
||||
CheckHealthCheckInterval(h *types.HealthCheckConfig)
|
||||
CheckHealthCheckRetries(h *types.HealthCheckConfig)
|
||||
CheckHealthCheckStartPeriod(h *types.HealthCheckConfig)
|
||||
CheckImage(service *types.ServiceConfig)
|
||||
CheckInit(service *types.ServiceConfig)
|
||||
CheckIpc(service *types.ServiceConfig)
|
||||
CheckIsolation(service *types.ServiceConfig)
|
||||
CheckLabels(service *types.ServiceConfig)
|
||||
CheckLinks(service *types.ServiceConfig)
|
||||
CheckLoggingDriver(logging *types.LoggingConfig)
|
||||
CheckLoggingOptions(logging *types.LoggingConfig)
|
||||
CheckMemLimit(service *types.ServiceConfig)
|
||||
CheckMemReservation(service *types.ServiceConfig)
|
||||
CheckMemSwapLimit(service *types.ServiceConfig)
|
||||
CheckMemSwappiness(service *types.ServiceConfig)
|
||||
CheckMacAddress(service *types.ServiceConfig)
|
||||
CheckNet(service *types.ServiceConfig)
|
||||
CheckNetworkMode(service *types.ServiceConfig)
|
||||
CheckNetworkAliases(n *types.ServiceNetworkConfig)
|
||||
CheckNetworkIpv4Address(n *types.ServiceNetworkConfig)
|
||||
CheckNetworkIpv6Address(n *types.ServiceNetworkConfig)
|
||||
CheckOomKillDisable(service *types.ServiceConfig)
|
||||
CheckOomScoreAdj(service *types.ServiceConfig)
|
||||
CheckPid(service *types.ServiceConfig)
|
||||
CheckPidsLimit(service *types.ServiceConfig)
|
||||
CheckPlatform(service *types.ServiceConfig)
|
||||
CheckPortsMode(p *types.ServicePortConfig)
|
||||
CheckPortsTarget(p *types.ServicePortConfig)
|
||||
CheckPortsPublished(p *types.ServicePortConfig)
|
||||
CheckPortsProtocol(p *types.ServicePortConfig)
|
||||
CheckPrivileged(service *types.ServiceConfig)
|
||||
CheckPullPolicy(service *types.ServiceConfig)
|
||||
CheckReadOnly(service *types.ServiceConfig)
|
||||
CheckRestart(service *types.ServiceConfig)
|
||||
CheckRuntime(service *types.ServiceConfig)
|
||||
CheckScale(service *types.ServiceConfig)
|
||||
CheckSecrets(service *types.ServiceConfig)
|
||||
CheckFileReferenceSource(s string, config *types.FileReferenceConfig)
|
||||
CheckFileReferenceTarget(s string, config *types.FileReferenceConfig)
|
||||
CheckFileReferenceUID(s string, config *types.FileReferenceConfig)
|
||||
CheckFileReferenceGID(s string, config *types.FileReferenceConfig)
|
||||
CheckFileReferenceMode(s string, config *types.FileReferenceConfig)
|
||||
CheckSecurityOpt(service *types.ServiceConfig)
|
||||
CheckShmSize(service *types.ServiceConfig)
|
||||
CheckStdinOpen(service *types.ServiceConfig)
|
||||
CheckStopGracePeriod(service *types.ServiceConfig)
|
||||
CheckStopSignal(service *types.ServiceConfig)
|
||||
CheckSysctls(service *types.ServiceConfig)
|
||||
CheckTmpfs(service *types.ServiceConfig)
|
||||
CheckTty(service *types.ServiceConfig)
|
||||
CheckUlimits(service *types.ServiceConfig)
|
||||
CheckUser(service *types.ServiceConfig)
|
||||
CheckUserNSMode(service *types.ServiceConfig)
|
||||
CheckUts(service *types.ServiceConfig)
|
||||
CheckVolumeDriver(service *types.ServiceConfig)
|
||||
CheckVolumesSource(config *types.ServiceVolumeConfig)
|
||||
CheckVolumesTarget(config *types.ServiceVolumeConfig)
|
||||
CheckVolumesReadOnly(config *types.ServiceVolumeConfig)
|
||||
CheckVolumesConsistency(config *types.ServiceVolumeConfig)
|
||||
CheckVolumesBind(config *types.ServiceVolumeBind)
|
||||
CheckVolumesVolume(config *types.ServiceVolumeVolume)
|
||||
CheckVolumesTmpfs(config *types.ServiceVolumeTmpfs)
|
||||
CheckVolumesFrom(service *types.ServiceConfig)
|
||||
CheckWorkingDir(service *types.ServiceConfig)
|
||||
CheckVolumeConfigDriver(config *types.VolumeConfig)
|
||||
CheckVolumeConfigDriverOpts(config *types.VolumeConfig)
|
||||
CheckVolumeConfigExternal(config *types.VolumeConfig)
|
||||
CheckVolumeConfigLabels(config *types.VolumeConfig)
|
||||
CheckFileObjectConfigFile(s string, config *types.FileObjectConfig)
|
||||
CheckFileObjectConfigExternal(s string, config *types.FileObjectConfig)
|
||||
CheckFileObjectConfigLabels(s string, config *types.FileObjectConfig)
|
||||
CheckFileObjectConfigDriver(s string, config *types.FileObjectConfig)
|
||||
CheckFileObjectConfigDriverOpts(s string, config *types.FileObjectConfig)
|
||||
CheckFileObjectConfigTemplateDriver(s string, config *types.FileObjectConfig)
|
||||
CheckDeploy(deploy *types.ServiceConfig) bool
|
||||
CheckDeployEndpointMode(deploy *types.DeployConfig)
|
||||
CheckDeployLabels(deploy *types.DeployConfig)
|
||||
CheckDeployMode(deploy *types.DeployConfig)
|
||||
CheckDeployReplicas(deploy *types.DeployConfig)
|
||||
CheckDeployRestartPolicy(deploy *types.DeployConfig) bool
|
||||
CheckDeployRollbackConfig(deploy *types.DeployConfig) bool
|
||||
CheckDeployUpdateConfig(deploy *types.DeployConfig) bool
|
||||
CheckPlacementConstraints(p *types.Placement)
|
||||
CheckPlacementMaxReplicas(p *types.Placement)
|
||||
CheckPlacementPreferences(p *types.Placement)
|
||||
CheckRestartPolicyDelay(policy *types.RestartPolicy)
|
||||
CheckRestartPolicyCondition(policy *types.RestartPolicy)
|
||||
CheckRestartPolicyMaxAttempts(policy *types.RestartPolicy)
|
||||
CheckRestartPolicyWindow(policy *types.RestartPolicy)
|
||||
CheckUpdateConfigDelay(rollback string, config *types.UpdateConfig)
|
||||
CheckUpdateConfigFailureAction(rollback string, config *types.UpdateConfig)
|
||||
CheckUpdateConfigMaxFailureRatio(rollback string, config *types.UpdateConfig)
|
||||
CheckUpdateConfigMonitor(rollback string, config *types.UpdateConfig)
|
||||
CheckUpdateConfigOrder(rollback string, config *types.UpdateConfig)
|
||||
CheckUpdateConfigParallelism(rollback string, config *types.UpdateConfig)
|
||||
CheckDeployResourcesNanoCPUs(s string, resource *types.Resource)
|
||||
CheckDeployResourcesMemoryBytes(s string, resource *types.Resource)
|
||||
CheckDeployResourcesDevices(s string, resource *types.Resource)
|
||||
CheckDeployResourcesDevicesCapabilities(s string, r types.DeviceRequest)
|
||||
CheckDeployResourcesDevicesCount(s string, r types.DeviceRequest)
|
||||
CheckDeployResourcesDevicesIDs(s string, r types.DeviceRequest)
|
||||
CheckDeployResourcesDevicesDriver(s string, r types.DeviceRequest)
|
||||
CheckDeployResourcesGenericResources(s string, resource *types.Resource)
|
||||
CheckDeployResourcesLimits(deploy *types.DeployConfig) bool
|
||||
CheckDeployResourcesReservations(deploy *types.DeployConfig) bool
|
||||
CheckHealthCheck(service *types.ServiceConfig) bool
|
||||
CheckLogging(service *types.ServiceConfig) bool
|
||||
CheckNetworks(service *types.ServiceConfig) bool
|
||||
CheckPorts(service *types.ServiceConfig) bool
|
||||
CheckServiceVolumes(service *types.ServiceConfig) bool
|
||||
CheckNetworkConfigIpam(network *types.NetworkConfig)
|
||||
CheckNetworkConfigIpamSubnet(config *types.IPAMPool)
|
||||
CheckNetworkConfigIpamGateway(config *types.IPAMPool)
|
||||
CheckNetworkConfigIpamIPRange(config *types.IPAMPool)
|
||||
CheckNetworkConfigIpamAuxiliaryAddresses(config *types.IPAMPool)
|
||||
CheckNetworkConfigDriver(network *types.NetworkConfig)
|
||||
CheckNetworkConfigDriverOpts(network *types.NetworkConfig)
|
||||
CheckNetworkConfigExternal(network *types.NetworkConfig)
|
||||
CheckNetworkConfigInternal(network *types.NetworkConfig)
|
||||
CheckNetworkConfigAttachable(network *types.NetworkConfig)
|
||||
CheckNetworkConfigLabels(network *types.NetworkConfig)
|
||||
}
|
||||
|
||||
func Check(project *types.Project, c Checker) {
|
||||
for i, service := range project.Services {
|
||||
CheckServiceConfig(&service, c)
|
||||
project.Services[i] = service
|
||||
}
|
||||
|
||||
for i, network := range project.Networks {
|
||||
CheckNetworkConfig(&network, c)
|
||||
project.Networks[i] = network
|
||||
}
|
||||
|
||||
for i, volume := range project.Volumes {
|
||||
CheckVolumeConfig(&volume, c)
|
||||
project.Volumes[i] = volume
|
||||
}
|
||||
|
||||
for i, config := range project.Configs {
|
||||
CheckConfigsConfig(&config, c)
|
||||
project.Configs[i] = config
|
||||
}
|
||||
|
||||
for i, secret := range project.Secrets {
|
||||
CheckSecretsConfig(&secret, c)
|
||||
project.Secrets[i] = secret
|
||||
}
|
||||
}
|
||||
|
||||
// IsCompatible return true if the checker didn't reported any incompatibility error
|
||||
func IsCompatible(c Checker) bool {
|
||||
for _, err := range c.Errors() {
|
||||
if errdefs.IsIncompatibleError(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func CheckServiceConfig(service *types.ServiceConfig, c Checker) {
|
||||
c.CheckBlkioConfig(service)
|
||||
if service.Build != nil && c.CheckBuild(service) {
|
||||
c.CheckBuildArgs(service.Build)
|
||||
c.CheckBuildLabels(service.Build)
|
||||
c.CheckBuildCacheFrom(service.Build)
|
||||
c.CheckBuildNetwork(service.Build)
|
||||
c.CheckBuildTarget(service.Build)
|
||||
}
|
||||
c.CheckCapAdd(service)
|
||||
c.CheckCapDrop(service)
|
||||
c.CheckCgroupParent(service)
|
||||
c.CheckCPUCount(service)
|
||||
c.CheckCPUPercent(service)
|
||||
c.CheckCPUPeriod(service)
|
||||
c.CheckCPUQuota(service)
|
||||
c.CheckCPURTPeriod(service)
|
||||
c.CheckCPURTRuntime(service)
|
||||
c.CheckCPUs(service)
|
||||
c.CheckCPUSet(service)
|
||||
c.CheckCPUShares(service)
|
||||
c.CheckCommand(service)
|
||||
c.CheckConfigs(service)
|
||||
c.CheckContainerName(service)
|
||||
c.CheckCredentialSpec(service)
|
||||
c.CheckDependsOn(service)
|
||||
if service.Deploy != nil && c.CheckDeploy(service) {
|
||||
c.CheckDeployEndpointMode(service.Deploy)
|
||||
c.CheckDeployLabels(service.Deploy)
|
||||
c.CheckDeployMode(service.Deploy)
|
||||
c.CheckPlacementConstraints(&service.Deploy.Placement)
|
||||
c.CheckPlacementMaxReplicas(&service.Deploy.Placement)
|
||||
c.CheckPlacementPreferences(&service.Deploy.Placement)
|
||||
c.CheckDeployReplicas(service.Deploy)
|
||||
if service.Deploy.Resources.Limits != nil && c.CheckDeployResourcesLimits(service.Deploy) {
|
||||
c.CheckDeployResourcesNanoCPUs(ResourceLimits, service.Deploy.Resources.Limits)
|
||||
c.CheckDeployResourcesMemoryBytes(ResourceLimits, service.Deploy.Resources.Limits)
|
||||
c.CheckDeployResourcesGenericResources(ResourceLimits, service.Deploy.Resources.Limits)
|
||||
}
|
||||
if service.Deploy.Resources.Reservations != nil && c.CheckDeployResourcesReservations(service.Deploy) {
|
||||
c.CheckDeployResourcesNanoCPUs(ResourceReservations, service.Deploy.Resources.Reservations)
|
||||
c.CheckDeployResourcesMemoryBytes(ResourceReservations, service.Deploy.Resources.Reservations)
|
||||
c.CheckDeployResourcesGenericResources(ResourceReservations, service.Deploy.Resources.Reservations)
|
||||
c.CheckDeployResourcesDevices(ResourceReservations, service.Deploy.Resources.Reservations)
|
||||
}
|
||||
if service.Deploy.RestartPolicy != nil && c.CheckDeployRestartPolicy(service.Deploy) {
|
||||
c.CheckRestartPolicyCondition(service.Deploy.RestartPolicy)
|
||||
c.CheckRestartPolicyDelay(service.Deploy.RestartPolicy)
|
||||
c.CheckRestartPolicyMaxAttempts(service.Deploy.RestartPolicy)
|
||||
c.CheckRestartPolicyWindow(service.Deploy.RestartPolicy)
|
||||
}
|
||||
if service.Deploy.UpdateConfig != nil && c.CheckDeployUpdateConfig(service.Deploy) {
|
||||
c.CheckUpdateConfigDelay(UpdateConfigUpdate, service.Deploy.UpdateConfig)
|
||||
c.CheckUpdateConfigFailureAction(UpdateConfigUpdate, service.Deploy.UpdateConfig)
|
||||
c.CheckUpdateConfigMaxFailureRatio(UpdateConfigUpdate, service.Deploy.UpdateConfig)
|
||||
c.CheckUpdateConfigMonitor(UpdateConfigUpdate, service.Deploy.UpdateConfig)
|
||||
c.CheckUpdateConfigOrder(UpdateConfigUpdate, service.Deploy.UpdateConfig)
|
||||
c.CheckUpdateConfigParallelism(UpdateConfigUpdate, service.Deploy.UpdateConfig)
|
||||
}
|
||||
if service.Deploy.RollbackConfig != nil && c.CheckDeployRollbackConfig(service.Deploy) {
|
||||
c.CheckUpdateConfigDelay(UpdateConfigRollback, service.Deploy.RollbackConfig)
|
||||
c.CheckUpdateConfigFailureAction(UpdateConfigRollback, service.Deploy.RollbackConfig)
|
||||
c.CheckUpdateConfigMaxFailureRatio(UpdateConfigRollback, service.Deploy.RollbackConfig)
|
||||
c.CheckUpdateConfigMonitor(UpdateConfigRollback, service.Deploy.RollbackConfig)
|
||||
c.CheckUpdateConfigOrder(UpdateConfigRollback, service.Deploy.RollbackConfig)
|
||||
c.CheckUpdateConfigParallelism(UpdateConfigRollback, service.Deploy.RollbackConfig)
|
||||
}
|
||||
}
|
||||
c.CheckDevices(service)
|
||||
c.CheckDNS(service)
|
||||
c.CheckDNSOpts(service)
|
||||
c.CheckDNSSearch(service)
|
||||
c.CheckDomainName(service)
|
||||
c.CheckEntrypoint(service)
|
||||
c.CheckEnvironment(service)
|
||||
c.CheckEnvFile(service)
|
||||
c.CheckExpose(service)
|
||||
c.CheckExtends(service)
|
||||
c.CheckExternalLinks(service)
|
||||
c.CheckExtraHosts(service)
|
||||
c.CheckGroupAdd(service)
|
||||
c.CheckHostname(service)
|
||||
if service.HealthCheck != nil && c.CheckHealthCheck(service) {
|
||||
c.CheckHealthCheckInterval(service.HealthCheck)
|
||||
c.CheckHealthCheckRetries(service.HealthCheck)
|
||||
c.CheckHealthCheckStartPeriod(service.HealthCheck)
|
||||
c.CheckHealthCheckTest(service.HealthCheck)
|
||||
c.CheckHealthCheckTimeout(service.HealthCheck)
|
||||
}
|
||||
c.CheckImage(service)
|
||||
c.CheckInit(service)
|
||||
c.CheckIpc(service)
|
||||
c.CheckIsolation(service)
|
||||
c.CheckLabels(service)
|
||||
c.CheckLinks(service)
|
||||
if service.Logging != nil && c.CheckLogging(service) {
|
||||
c.CheckLoggingDriver(service.Logging)
|
||||
c.CheckLoggingOptions(service.Logging)
|
||||
}
|
||||
c.CheckMemLimit(service)
|
||||
c.CheckMemReservation(service)
|
||||
c.CheckMemSwapLimit(service)
|
||||
c.CheckMemSwappiness(service)
|
||||
c.CheckMacAddress(service)
|
||||
c.CheckNet(service)
|
||||
c.CheckNetworkMode(service)
|
||||
if len(service.Networks) > 0 && c.CheckNetworks(service) {
|
||||
for _, n := range service.Networks {
|
||||
if n != nil {
|
||||
c.CheckNetworkAliases(n)
|
||||
c.CheckNetworkIpv4Address(n)
|
||||
c.CheckNetworkIpv6Address(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
c.CheckOomKillDisable(service)
|
||||
c.CheckOomScoreAdj(service)
|
||||
c.CheckPid(service)
|
||||
c.CheckPidsLimit(service)
|
||||
c.CheckPlatform(service)
|
||||
if len(service.Ports) > 0 && c.CheckPorts(service) {
|
||||
for i, p := range service.Ports {
|
||||
c.CheckPortsMode(&p)
|
||||
c.CheckPortsTarget(&p)
|
||||
c.CheckPortsProtocol(&p)
|
||||
c.CheckPortsPublished(&p)
|
||||
service.Ports[i] = p
|
||||
}
|
||||
}
|
||||
c.CheckPrivileged(service)
|
||||
c.CheckPullPolicy(service)
|
||||
c.CheckReadOnly(service)
|
||||
c.CheckRestart(service)
|
||||
c.CheckRuntime(service)
|
||||
c.CheckScale(service)
|
||||
c.CheckSecrets(service)
|
||||
c.CheckSecurityOpt(service)
|
||||
c.CheckShmSize(service)
|
||||
c.CheckStdinOpen(service)
|
||||
c.CheckStopGracePeriod(service)
|
||||
c.CheckStopSignal(service)
|
||||
c.CheckSysctls(service)
|
||||
c.CheckTmpfs(service)
|
||||
c.CheckTty(service)
|
||||
c.CheckUlimits(service)
|
||||
c.CheckUser(service)
|
||||
c.CheckUserNSMode(service)
|
||||
c.CheckUts(service)
|
||||
c.CheckVolumeDriver(service)
|
||||
if len(service.Volumes) > 0 && c.CheckServiceVolumes(service) {
|
||||
for i, v := range service.Volumes {
|
||||
c.CheckVolumesSource(&v)
|
||||
c.CheckVolumesTarget(&v)
|
||||
c.CheckVolumesReadOnly(&v)
|
||||
switch v.Type {
|
||||
case types.VolumeTypeBind:
|
||||
c.CheckVolumesBind(v.Bind)
|
||||
case types.VolumeTypeVolume:
|
||||
c.CheckVolumesVolume(v.Volume)
|
||||
case types.VolumeTypeTmpfs:
|
||||
c.CheckVolumesTmpfs(v.Tmpfs)
|
||||
}
|
||||
service.Volumes[i] = v
|
||||
}
|
||||
}
|
||||
c.CheckVolumesFrom(service)
|
||||
c.CheckWorkingDir(service)
|
||||
}
|
||||
|
||||
func CheckNetworkConfig(network *types.NetworkConfig, c Checker) {
|
||||
c.CheckNetworkConfigDriver(network)
|
||||
c.CheckNetworkConfigDriverOpts(network)
|
||||
c.CheckNetworkConfigIpam(network)
|
||||
c.CheckNetworkConfigExternal(network)
|
||||
c.CheckNetworkConfigInternal(network)
|
||||
c.CheckNetworkConfigAttachable(network)
|
||||
c.CheckNetworkConfigLabels(network)
|
||||
}
|
||||
|
||||
func CheckVolumeConfig(config *types.VolumeConfig, c Checker) {
|
||||
c.CheckVolumeConfigDriver(config)
|
||||
c.CheckVolumeConfigDriverOpts(config)
|
||||
c.CheckVolumeConfigExternal(config)
|
||||
c.CheckVolumeConfigLabels(config)
|
||||
}
|
||||
|
||||
func CheckConfigsConfig(config *types.ConfigObjConfig, c Checker) {
|
||||
ref := types.FileObjectConfig(*config)
|
||||
CheckFileObjectConfig("configs", &ref, c)
|
||||
}
|
||||
|
||||
func CheckSecretsConfig(config *types.SecretConfig, c Checker) {
|
||||
ref := types.FileObjectConfig(*config)
|
||||
CheckFileObjectConfig("secrets", &ref, c)
|
||||
}
|
||||
|
||||
func CheckFileObjectConfig(s string, config *types.FileObjectConfig, c Checker) {
|
||||
c.CheckFileObjectConfigDriver(s, config)
|
||||
c.CheckFileObjectConfigDriverOpts(s, config)
|
||||
c.CheckFileObjectConfigExternal(s, config)
|
||||
c.CheckFileObjectConfigFile(s, config)
|
||||
c.CheckFileObjectConfigLabels(s, config)
|
||||
c.CheckFileObjectConfigTemplateDriver(s, config)
|
||||
}
|
71
pkg/third_party/compose-go/compatibility/configs.go
vendored
Executable file
71
pkg/third_party/compose-go/compatibility/configs.go
vendored
Executable file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
func (c *AllowList) CheckFileObjectConfigFile(s string, config *types.FileObjectConfig) {
|
||||
k := fmt.Sprintf("%s.file", s)
|
||||
if !c.supported(k) && config.File != "" {
|
||||
config.File = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileObjectConfigExternal(s string, config *types.FileObjectConfig) {
|
||||
k := fmt.Sprintf("%s.external", s)
|
||||
if !c.supported(k) && config.External.External {
|
||||
config.External.External = false
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileObjectConfigLabels(s string, config *types.FileObjectConfig) {
|
||||
k := fmt.Sprintf("%s.labels", s)
|
||||
if !c.supported(k) && len(config.Labels) != 0 {
|
||||
config.Labels = nil
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileObjectConfigDriver(s string, config *types.FileObjectConfig) {
|
||||
k := fmt.Sprintf("%s.driver", s)
|
||||
if !c.supported(k) && config.Driver != "" {
|
||||
config.Driver = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileObjectConfigDriverOpts(s string, config *types.FileObjectConfig) {
|
||||
k := fmt.Sprintf("%s.driver_opts", s)
|
||||
if !c.supported(k) && len(config.DriverOpts) != 0 {
|
||||
config.DriverOpts = nil
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileObjectConfigTemplateDriver(s string, config *types.FileObjectConfig) {
|
||||
k := fmt.Sprintf("%s.template_driver", s)
|
||||
if !c.supported(k) && config.TemplateDriver != "" {
|
||||
config.TemplateDriver = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
275
pkg/third_party/compose-go/compatibility/deploy.go
vendored
Executable file
275
pkg/third_party/compose-go/compatibility/deploy.go
vendored
Executable file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
func (c *AllowList) CheckDeploy(service *types.ServiceConfig) bool {
|
||||
if !c.supported("services.deploy") && service.Deploy != nil {
|
||||
service.Deploy = nil
|
||||
c.Unsupported("services.deploy")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployMode(config *types.DeployConfig) {
|
||||
if !c.supported("services.deploy.mode") && config.Mode != "" {
|
||||
config.Mode = ""
|
||||
c.Unsupported("services.deploy.mode")
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckDeployReplicas(config *types.DeployConfig) {
|
||||
if !c.supported("services.deploy.replicas") && config.Replicas != nil {
|
||||
config.Replicas = nil
|
||||
c.Unsupported("services.deploy.replicas")
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckDeployLabels(config *types.DeployConfig) {
|
||||
if !c.supported("services.deploy.labels") && len(config.Labels) != 0 {
|
||||
config.Labels = nil
|
||||
c.Unsupported("services.deploy.labels")
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
UpdateConfigUpdate = "update_config"
|
||||
UpdateConfigRollback = "rolback_config"
|
||||
)
|
||||
|
||||
func (c *AllowList) CheckDeployUpdateConfig(config *types.DeployConfig) bool {
|
||||
if !c.supported("services.deploy.update_config") {
|
||||
config.UpdateConfig = nil
|
||||
c.Unsupported("services.deploy.update_config")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployRollbackConfig(config *types.DeployConfig) bool {
|
||||
if !c.supported("services.deploy.rollback_config") {
|
||||
config.RollbackConfig = nil
|
||||
c.Unsupported("services.deploy.rollback_config")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckUpdateConfigParallelism(s string, config *types.UpdateConfig) {
|
||||
k := fmt.Sprintf("services.deploy.%s.parallelism", s)
|
||||
if !c.supported(k) && config.Parallelism != nil {
|
||||
config.Parallelism = nil
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckUpdateConfigDelay(s string, config *types.UpdateConfig) {
|
||||
k := fmt.Sprintf("services.deploy.%s.delay", s)
|
||||
if !c.supported(k) && config.Delay != 0 {
|
||||
config.Delay = 0
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckUpdateConfigFailureAction(s string, config *types.UpdateConfig) {
|
||||
k := fmt.Sprintf("services.deploy.%s.failure_action", s)
|
||||
if !c.supported(k) && config.FailureAction != "" {
|
||||
config.FailureAction = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckUpdateConfigMonitor(s string, config *types.UpdateConfig) {
|
||||
k := fmt.Sprintf("services.deploy.%s.monitor", s)
|
||||
if !c.supported(k) && config.Monitor != 0 {
|
||||
config.Monitor = 0
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckUpdateConfigMaxFailureRatio(s string, config *types.UpdateConfig) {
|
||||
k := fmt.Sprintf("services.deploy.%s.max_failure_ratio", s)
|
||||
if !c.supported(k) && config.MaxFailureRatio != 0 {
|
||||
config.MaxFailureRatio = 0
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckUpdateConfigOrder(s string, config *types.UpdateConfig) {
|
||||
k := fmt.Sprintf("services.deploy.%s.order", s)
|
||||
if !c.supported(k) && config.Order != "" {
|
||||
config.Order = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
ResourceLimits = "limits"
|
||||
ResourceReservations = "reservations"
|
||||
)
|
||||
|
||||
func (c *AllowList) CheckDeployResourcesLimits(config *types.DeployConfig) bool {
|
||||
if !c.supported("services.deploy.resources.limits") {
|
||||
config.Resources.Limits = nil
|
||||
c.Unsupported("services.deploy.resources.limits")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployResourcesReservations(config *types.DeployConfig) bool {
|
||||
if !c.supported("services.deploy.resources.reservations") {
|
||||
config.Resources.Reservations = nil
|
||||
c.Unsupported("services.deploy.resources.reservations")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployResourcesNanoCPUs(s string, r *types.Resource) {
|
||||
k := fmt.Sprintf("services.deploy.resources.%s.cpus", s)
|
||||
if !c.supported(k) && r.NanoCPUs != "" {
|
||||
r.NanoCPUs = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckDeployResourcesMemoryBytes(s string, r *types.Resource) {
|
||||
k := fmt.Sprintf("services.deploy.resources.%s.memory", s)
|
||||
if !c.supported(k) && r.MemoryBytes != 0 {
|
||||
r.MemoryBytes = 0
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployResourcesDevices(s string, r *types.Resource) {
|
||||
if len(r.Devices) == 0 {
|
||||
return
|
||||
}
|
||||
k := fmt.Sprintf("services.deploy.resources.%s.devices", s)
|
||||
if !c.supported(k) {
|
||||
r.Devices = nil
|
||||
c.Unsupported(k)
|
||||
return
|
||||
}
|
||||
for _, d := range r.Devices {
|
||||
c.CheckDeployResourcesDevicesCapabilities(s, d)
|
||||
c.CheckDeployResourcesDevicesCount(s, d)
|
||||
c.CheckDeployResourcesDevicesIDs(s, d)
|
||||
c.CheckDeployResourcesDevicesDriver(s, d)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployResourcesDevicesCapabilities(s string, r types.DeviceRequest) {
|
||||
k := fmt.Sprintf("services.deploy.resources.%s.devices.capabilities", s)
|
||||
if !c.supported(k) && len(r.Capabilities) != 0 {
|
||||
r.Capabilities = nil
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployResourcesDevicesCount(s string, r types.DeviceRequest) {
|
||||
k := fmt.Sprintf("services.deploy.resources.%s.devices.count", s)
|
||||
if !c.supported(k) && r.Count != 0 {
|
||||
r.Count = 0
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployResourcesDevicesIDs(s string, r types.DeviceRequest) {
|
||||
k := fmt.Sprintf("services.deploy.resources.%s.devices.device_ids", s)
|
||||
if !c.supported(k) && len(r.IDs) != 0 {
|
||||
r.IDs = nil
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployResourcesDevicesDriver(s string, r types.DeviceRequest) {
|
||||
k := fmt.Sprintf("services.deploy.resources.%s.devices.driver", s)
|
||||
if !c.supported(k) && r.Driver != "" {
|
||||
r.Driver = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployResourcesGenericResources(s string, r *types.Resource) {
|
||||
k := fmt.Sprintf("services.deploy.resources.%s.generic_resources", s)
|
||||
if !c.supported(k) && len(r.GenericResources) != 0 {
|
||||
r.GenericResources = nil
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployRestartPolicy(config *types.DeployConfig) bool {
|
||||
if !c.supported("services.deploy.restart_policy") {
|
||||
config.RestartPolicy = nil
|
||||
c.Unsupported("services.deploy.restart_policy")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckRestartPolicyCondition(p *types.RestartPolicy) {
|
||||
if !c.supported("services.deploy.restart_policy.condition") && p.Condition != "" {
|
||||
p.Condition = ""
|
||||
c.Unsupported("services.deploy.restart_policy.condition")
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckRestartPolicyDelay(p *types.RestartPolicy) {
|
||||
if !c.supported("services.deploy.restart_policy.delay") && p.Delay != nil {
|
||||
p.Delay = nil
|
||||
c.Unsupported("services.deploy.restart_policy.delay")
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckRestartPolicyMaxAttempts(p *types.RestartPolicy) {
|
||||
if !c.supported("services.deploy.restart_policy.max_attempts") && p.MaxAttempts != nil {
|
||||
p.MaxAttempts = nil
|
||||
c.Unsupported("services.deploy.restart_policy.max_attempts")
|
||||
}
|
||||
}
|
||||
func (c *AllowList) CheckRestartPolicyWindow(p *types.RestartPolicy) {
|
||||
if !c.supported("services.deploy.restart_policy.window") && p.Window != nil {
|
||||
p.Window = nil
|
||||
c.Unsupported("services.deploy.restart_policy.window")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPlacementConstraints(p *types.Placement) {
|
||||
if !c.supported("services.deploy.placement", "services.deploy.placement.constraints") && len(p.Constraints) != 0 {
|
||||
p.Constraints = nil
|
||||
c.Unsupported("services.deploy.restart_policy.constraints")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPlacementPreferences(p *types.Placement) {
|
||||
if !c.supported("services.deploy.placement", "services.deploy.placement.preferences") && p.Preferences != nil {
|
||||
p.Preferences = nil
|
||||
c.Unsupported("services.deploy.restart_policy.preferences")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPlacementMaxReplicas(p *types.Placement) {
|
||||
if !c.supported("services.deploy.placement", "services.deploy.placement.max_replicas_per_node") && p.MaxReplicas != 0 {
|
||||
p.MaxReplicas = 0
|
||||
c.Unsupported("services.deploy.restart_policy.max_replicas_per_node")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDeployEndpointMode(config *types.DeployConfig) {
|
||||
if !c.supported("services.deploy.endpoint_mode") && config.EndpointMode != "" {
|
||||
config.EndpointMode = ""
|
||||
c.Unsupported("services.deploy.endpoint_mode")
|
||||
}
|
||||
}
|
122
pkg/third_party/compose-go/compatibility/networks.go
vendored
Executable file
122
pkg/third_party/compose-go/compatibility/networks.go
vendored
Executable file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compatibility
|
||||
|
||||
import "github.com/compose-spec/compose-go/types"
|
||||
|
||||
func (c *AllowList) CheckNetworkConfig(network *types.NetworkConfig) {
|
||||
c.CheckNetworkConfigDriver(network)
|
||||
c.CheckNetworkConfigDriverOpts(network)
|
||||
c.CheckNetworkConfigIpam(network)
|
||||
c.CheckNetworkConfigExternal(network)
|
||||
c.CheckNetworkConfigInternal(network)
|
||||
c.CheckNetworkConfigAttachable(network)
|
||||
c.CheckNetworkConfigLabels(network)
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigDriver(config *types.NetworkConfig) {
|
||||
if !c.supported("networks.driver") && config.Driver != "" {
|
||||
config.Driver = ""
|
||||
c.Unsupported("networks.driver")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigDriverOpts(config *types.NetworkConfig) {
|
||||
if !c.supported("networks.driver_opts") && len(config.DriverOpts) != 0 {
|
||||
config.DriverOpts = nil
|
||||
c.Unsupported("networks.driver_opts")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigIpam(config *types.NetworkConfig) {
|
||||
c.CheckNetworkConfigIpamDriver(&config.Ipam)
|
||||
if len(config.Ipam.Config) != 0 {
|
||||
if !c.supported("networks.ipam.config") {
|
||||
c.Unsupported("networks.ipam.config")
|
||||
return
|
||||
}
|
||||
for _, p := range config.Ipam.Config {
|
||||
c.CheckNetworkConfigIpamSubnet(p)
|
||||
c.CheckNetworkConfigIpamGateway(p)
|
||||
c.CheckNetworkConfigIpamIPRange(p)
|
||||
c.CheckNetworkConfigIpamAuxiliaryAddresses(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigIpamDriver(config *types.IPAMConfig) {
|
||||
if !c.supported("networks.ipam.driver") && config.Driver != "" {
|
||||
config.Driver = ""
|
||||
c.Unsupported("networks.ipam.driver")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigIpamSubnet(config *types.IPAMPool) {
|
||||
if !c.supported("networks.ipam.config.subnet") && config.Subnet != "" {
|
||||
config.Subnet = ""
|
||||
c.Unsupported("networks.ipam.config.subnet")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigIpamGateway(config *types.IPAMPool) {
|
||||
if !c.supported("networks.ipam.config.gateway") && config.Gateway != "" {
|
||||
config.Gateway = ""
|
||||
c.Unsupported("networks.ipam.config.gateway")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigIpamIPRange(config *types.IPAMPool) {
|
||||
if !c.supported("networks.ipam.config.ip_range") && config.IPRange != "" {
|
||||
config.IPRange = ""
|
||||
c.Unsupported("networks.ipam.config.ip_range")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigIpamAuxiliaryAddresses(config *types.IPAMPool) {
|
||||
if !c.supported("networks.ipam.config.aux_addresses") && len(config.AuxiliaryAddresses) > 0 {
|
||||
config.AuxiliaryAddresses = nil
|
||||
c.Unsupported("networks.ipam.config.aux_addresses")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigExternal(config *types.NetworkConfig) {
|
||||
if !c.supported("networks.external") && config.External.External {
|
||||
config.External.External = false
|
||||
c.Unsupported("networks.external")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigInternal(config *types.NetworkConfig) {
|
||||
if !c.supported("networks.internal") && config.Internal {
|
||||
config.Internal = false
|
||||
c.Unsupported("networks.internal")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigAttachable(config *types.NetworkConfig) {
|
||||
if !c.supported("networks.attachable") && config.Attachable {
|
||||
config.Attachable = false
|
||||
c.Unsupported("networks.attachable")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkConfigLabels(config *types.NetworkConfig) {
|
||||
if !c.supported("networks.labels") && len(config.Labels) != 0 {
|
||||
config.Labels = nil
|
||||
c.Unsupported("networks.labels")
|
||||
}
|
||||
}
|
833
pkg/third_party/compose-go/compatibility/services.go
vendored
Executable file
833
pkg/third_party/compose-go/compatibility/services.go
vendored
Executable file
@ -0,0 +1,833 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
func (c *AllowList) CheckBlkioConfig(service *types.ServiceConfig) {
|
||||
if !c.supported("services.blkio_config") && service.BlkioConfig != nil {
|
||||
service.BlkioConfig = nil
|
||||
c.Unsupported("services.blkio_config")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBlkioWeight(config *types.BlkioConfig) {
|
||||
if !c.supported("services.blkio_config.weight") && config.Weight != 0 {
|
||||
config.Weight = 0
|
||||
c.Unsupported("services.blkio_config.weight")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBlkioWeightDevice(config *types.BlkioConfig) {
|
||||
if !c.supported("services.blkio_config.weight_device") && len(config.WeightDevice) != 0 {
|
||||
config.WeightDevice = nil
|
||||
c.Unsupported("services.blkio_config.weight_device")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBlkioDeviceReadBps(config *types.BlkioConfig) {
|
||||
if !c.supported("services.blkio_config.device_read_bps") && len(config.DeviceWriteBps) != 0 {
|
||||
config.DeviceWriteBps = nil
|
||||
c.Unsupported("services.blkio_config.device_read_bps")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBlkioDeviceReadIOps(config *types.BlkioConfig) {
|
||||
if !c.supported("services.blkio_config.device_read_iops") && len(config.DeviceReadIOps) != 0 {
|
||||
config.DeviceReadIOps = nil
|
||||
c.Unsupported("services.blkio_config.device_read_iops")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBlkioDeviceWriteBps(config *types.BlkioConfig) {
|
||||
if !c.supported("services.blkio_config.device_write_bps") && len(config.DeviceWriteBps) != 0 {
|
||||
config.DeviceWriteBps = nil
|
||||
c.Unsupported("services.blkio_config.device_write_bps")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckBlkioDeviceWriteIOps(config *types.BlkioConfig) {
|
||||
if !c.supported("services.blkio_config.device_write_iops") && len(config.DeviceWriteIOps) != 0 {
|
||||
config.DeviceWriteIOps = nil
|
||||
c.Unsupported("services.blkio_config.device_write_iops")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCapAdd(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cap_add") && len(service.CapAdd) != 0 {
|
||||
service.CapAdd = nil
|
||||
c.Unsupported("services.cap_add")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCapDrop(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cap_drop") && len(service.CapDrop) != 0 {
|
||||
service.CapDrop = nil
|
||||
c.Unsupported("services.cap_drop")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCgroupParent(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cgroup_parent") && service.CgroupParent != "" {
|
||||
service.CgroupParent = ""
|
||||
c.Unsupported("services.cgroup_parent")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCPUQuota(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cpu_quota") && service.CPUQuota != 0 {
|
||||
service.CPUQuota = 0
|
||||
c.Unsupported("services.cpu_quota")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCPUCount(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cpu_count") && service.CPUCount != 0 {
|
||||
service.CPUCount = 0
|
||||
c.Unsupported("services.cpu_count")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCPUPercent(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cpu_percent") && service.CPUPercent != 0 {
|
||||
service.CPUPercent = 0
|
||||
c.Unsupported("services.cpu_percent")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCPUPeriod(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cpu_period") && service.CPUPeriod != 0 {
|
||||
service.CPUPeriod = 0
|
||||
c.Unsupported("services.cpu_period")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCPURTRuntime(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cpu_rt_runtime") && service.CPURTRuntime != 0 {
|
||||
service.CPURTRuntime = 0
|
||||
c.Unsupported("services.cpu_rt_period")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCPURTPeriod(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cpu_rt_period") && service.CPURTPeriod != 0 {
|
||||
service.CPURTPeriod = 0
|
||||
c.Unsupported("services.cpu_rt_period")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCPUs(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cpus") && service.CPUS != 0 {
|
||||
service.CPUS = 0
|
||||
c.Unsupported("services.cpus")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCPUSet(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cpuset") && service.CPUSet != "" {
|
||||
service.CPUSet = ""
|
||||
c.Unsupported("services.cpuset")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCPUShares(service *types.ServiceConfig) {
|
||||
if !c.supported("services.cpu_shares") && service.CPUShares != 0 {
|
||||
service.CPUShares = 0
|
||||
c.Unsupported("services.cpu_shares")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCommand(service *types.ServiceConfig) {
|
||||
if !c.supported("services.command") && len(service.Command) != 0 {
|
||||
service.Command = nil
|
||||
c.Unsupported("services.command")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckConfigs(service *types.ServiceConfig) {
|
||||
if len(service.Configs) != 0 {
|
||||
if !c.supported("services.configs") {
|
||||
service.Configs = nil
|
||||
c.Unsupported("services.configs")
|
||||
return
|
||||
}
|
||||
for i, s := range service.Secrets {
|
||||
ref := types.FileReferenceConfig(s)
|
||||
c.CheckFileReference("configs", &ref)
|
||||
service.Secrets[i] = s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckContainerName(service *types.ServiceConfig) {
|
||||
if !c.supported("services.container_name") && service.ContainerName != "" {
|
||||
service.ContainerName = ""
|
||||
c.Unsupported("services.container_name")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckCredentialSpec(service *types.ServiceConfig) {
|
||||
if !c.supported("services.credential_spec") && service.CredentialSpec != nil {
|
||||
service.CredentialSpec = nil
|
||||
c.Unsupported("services.credential_spec")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDependsOn(service *types.ServiceConfig) {
|
||||
if !c.supported("services.depends_on") && len(service.DependsOn) != 0 {
|
||||
service.DependsOn = nil
|
||||
c.Unsupported("services.depends_on")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDevices(service *types.ServiceConfig) {
|
||||
if !c.supported("services.devices") && len(service.Devices) != 0 {
|
||||
service.Devices = nil
|
||||
c.Unsupported("services.devices")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDNS(service *types.ServiceConfig) {
|
||||
if !c.supported("services.dns") && service.DNS != nil {
|
||||
service.DNS = nil
|
||||
c.Unsupported("services.dns")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDNSOpts(service *types.ServiceConfig) {
|
||||
if !c.supported("services.dns_opt") && len(service.DNSOpts) != 0 {
|
||||
service.DNSOpts = nil
|
||||
c.Unsupported("services.dns_opt")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDNSSearch(service *types.ServiceConfig) {
|
||||
if !c.supported("services.dns_search") && len(service.DNSSearch) != 0 {
|
||||
service.DNSSearch = nil
|
||||
c.Unsupported("services.dns_search")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckDomainName(service *types.ServiceConfig) {
|
||||
if !c.supported("services.domainname") && service.DomainName != "" {
|
||||
service.DomainName = ""
|
||||
c.Unsupported("services.domainname")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckEntrypoint(service *types.ServiceConfig) {
|
||||
if !c.supported("services.entrypoint") && len(service.Entrypoint) != 0 {
|
||||
service.Entrypoint = nil
|
||||
c.Unsupported("services.entrypoint")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckEnvironment(service *types.ServiceConfig) {
|
||||
if !c.supported("services.environment") && len(service.Environment) != 0 {
|
||||
service.Environment = nil
|
||||
c.Unsupported("services.environment")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckEnvFile(service *types.ServiceConfig) {
|
||||
if !c.supported("services.env_file") && len(service.EnvFile) != 0 {
|
||||
service.EnvFile = nil
|
||||
c.Unsupported("services.env_file")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckExpose(service *types.ServiceConfig) {
|
||||
if !c.supported("services.expose") && len(service.Expose) != 0 {
|
||||
service.Expose = nil
|
||||
c.Unsupported("services.expose")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckExtends(service *types.ServiceConfig) {
|
||||
if !c.supported("services.extends") && len(service.Extends) != 0 {
|
||||
service.Extends = nil
|
||||
c.Unsupported("services.extends")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckExternalLinks(service *types.ServiceConfig) {
|
||||
if !c.supported("services.external_links") && len(service.ExternalLinks) != 0 {
|
||||
service.ExternalLinks = nil
|
||||
c.Unsupported("services.external_links")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckExtraHosts(service *types.ServiceConfig) {
|
||||
if !c.supported("services.extra_hosts") && len(service.ExtraHosts) != 0 {
|
||||
service.ExtraHosts = nil
|
||||
c.Unsupported("services.extra_hosts")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckGroupAdd(service *types.ServiceConfig) {
|
||||
if !c.supported("services.group_app") && len(service.GroupAdd) != 0 {
|
||||
service.GroupAdd = nil
|
||||
c.Unsupported("services.group_app")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckHostname(service *types.ServiceConfig) {
|
||||
if !c.supported("services.hostname") && service.Hostname != "" {
|
||||
service.Hostname = ""
|
||||
c.Unsupported("services.hostname")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckHealthCheck(service *types.ServiceConfig) bool {
|
||||
if !c.supported("services.healthcheck") {
|
||||
service.HealthCheck = nil
|
||||
c.Unsupported("services.healthcheck")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckHealthCheckTest(h *types.HealthCheckConfig) {
|
||||
if !c.supported("services.healthcheck.test") && len(h.Test) != 0 {
|
||||
h.Test = nil
|
||||
c.Unsupported("services.healthcheck.test")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckHealthCheckTimeout(h *types.HealthCheckConfig) {
|
||||
if !c.supported("services.healthcheck.timeout") && h.Timeout != nil {
|
||||
h.Timeout = nil
|
||||
c.Unsupported("services.healthcheck.timeout")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckHealthCheckInterval(h *types.HealthCheckConfig) {
|
||||
if !c.supported("services.healthcheck.interval") && h.Interval != nil {
|
||||
h.Interval = nil
|
||||
c.Unsupported("services.healthcheck.interval")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckHealthCheckRetries(h *types.HealthCheckConfig) {
|
||||
if !c.supported("services.healthcheck.retries") && h.Retries != nil {
|
||||
h.Retries = nil
|
||||
c.Unsupported("services.healthcheck.retries")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckHealthCheckStartPeriod(h *types.HealthCheckConfig) {
|
||||
if !c.supported("services.healthcheck.start_period") && h.StartPeriod != nil {
|
||||
h.StartPeriod = nil
|
||||
c.Unsupported("services.healthcheck.start_period")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckImage(service *types.ServiceConfig) {
|
||||
if !c.supported("services.image") && service.Image != "" {
|
||||
service.Image = ""
|
||||
c.Unsupported("services.image")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckInit(service *types.ServiceConfig) {
|
||||
if !c.supported("services.init") && service.Init != nil {
|
||||
service.Init = nil
|
||||
c.Unsupported("services.init")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckIpc(service *types.ServiceConfig) {
|
||||
if !c.supported("services.ipc") && service.Ipc != "" {
|
||||
service.Ipc = ""
|
||||
c.Unsupported("services.ipc")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckIsolation(service *types.ServiceConfig) {
|
||||
if !c.supported("services.isolation") && service.Isolation != "" {
|
||||
service.Isolation = ""
|
||||
c.Unsupported("services.isolation")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckLabels(service *types.ServiceConfig) {
|
||||
if !c.supported("services.labels") && len(service.Labels) != 0 {
|
||||
service.Labels = nil
|
||||
c.Unsupported("services.labels")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckLinks(service *types.ServiceConfig) {
|
||||
if !c.supported("services.links") && len(service.Links) != 0 {
|
||||
service.Links = nil
|
||||
c.Unsupported("services.links")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckLogging(service *types.ServiceConfig) bool {
|
||||
if !c.supported("services.logging") {
|
||||
service.Logging = nil
|
||||
c.Unsupported("services.logging")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckLoggingDriver(logging *types.LoggingConfig) {
|
||||
if !c.supported("services.logging.driver") && logging.Driver != "" {
|
||||
logging.Driver = ""
|
||||
c.Unsupported("services.logging.driver")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckLoggingOptions(logging *types.LoggingConfig) {
|
||||
if !c.supported("services.logging.options") && len(logging.Options) != 0 {
|
||||
logging.Options = nil
|
||||
c.Unsupported("services.logging.options")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckMemLimit(service *types.ServiceConfig) {
|
||||
if !c.supported("services.mem_limit") && service.MemLimit != 0 {
|
||||
service.MemLimit = 0
|
||||
c.Unsupported("services.mem_limit")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckMemReservation(service *types.ServiceConfig) {
|
||||
if !c.supported("services.mem_reservation") && service.MemReservation != 0 {
|
||||
service.MemReservation = 0
|
||||
c.Unsupported("services.mem_reservation")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckMemSwapLimit(service *types.ServiceConfig) {
|
||||
if !c.supported("services.memswap_limit") && service.MemSwapLimit != 0 {
|
||||
service.MemSwapLimit = 0
|
||||
c.Unsupported("services.memswap_limit")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckMemSwappiness(service *types.ServiceConfig) {
|
||||
if !c.supported("services.mem_swappiness") && service.MemSwappiness != 0 {
|
||||
service.MemSwappiness = 0
|
||||
c.Unsupported("services.mem_swappiness")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckMacAddress(service *types.ServiceConfig) {
|
||||
if !c.supported("services.mac_address") && service.MacAddress != "" {
|
||||
service.MacAddress = ""
|
||||
c.Unsupported("services.mac_address")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNet(service *types.ServiceConfig) {
|
||||
if !c.supported("services.net") && service.Net != "" {
|
||||
service.Net = ""
|
||||
c.Unsupported("services.net")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkMode(service *types.ServiceConfig) {
|
||||
if !c.supported("services.network_mode") && service.NetworkMode != "" {
|
||||
service.NetworkMode = ""
|
||||
c.Unsupported("services.network_mode")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworks(service *types.ServiceConfig) bool {
|
||||
if !c.supported("services.networks") {
|
||||
service.Networks = nil
|
||||
c.Unsupported("services.networks")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkAliases(n *types.ServiceNetworkConfig) {
|
||||
if !c.supported("services.networks.aliases") && len(n.Aliases) != 0 {
|
||||
n.Aliases = nil
|
||||
c.Unsupported("services.networks.aliases")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkIpv4Address(n *types.ServiceNetworkConfig) {
|
||||
if !c.supported("services.networks.ipv4_address") && n.Ipv4Address != "" {
|
||||
n.Ipv4Address = ""
|
||||
c.Unsupported("services.networks.ipv4_address")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckNetworkIpv6Address(n *types.ServiceNetworkConfig) {
|
||||
if !c.supported("services.networks.ipv6_address") && n.Ipv6Address != "" {
|
||||
n.Ipv6Address = ""
|
||||
c.Unsupported("services.networks.ipv6_address")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckOomKillDisable(service *types.ServiceConfig) {
|
||||
if !c.supported("services.oom_kill_disable") && service.OomKillDisable {
|
||||
service.OomKillDisable = false
|
||||
c.Unsupported("services.oom_kill_disable")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckOomScoreAdj(service *types.ServiceConfig) {
|
||||
if !c.supported("services.oom_score_adj") && service.OomScoreAdj != 0 {
|
||||
service.OomScoreAdj = 0
|
||||
c.Unsupported("services.oom_score_adj")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPid(service *types.ServiceConfig) {
|
||||
if !c.supported("services.pid") && service.Pid != "" {
|
||||
service.Pid = ""
|
||||
c.Unsupported("services.pid")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPidsLimit(service *types.ServiceConfig) {
|
||||
if !c.supported("services.pids_limit") && service.PidsLimit != 0 {
|
||||
service.PidsLimit = 0
|
||||
c.Unsupported("services.pids_limit")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPlatform(service *types.ServiceConfig) {
|
||||
if !c.supported("services.platform") && service.Platform != "" {
|
||||
service.Platform = ""
|
||||
c.Unsupported("services.platform")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPorts(service *types.ServiceConfig) bool {
|
||||
if !c.supported("services.ports") {
|
||||
service.Ports = nil
|
||||
c.Unsupported("services.ports")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPortsMode(p *types.ServicePortConfig) {
|
||||
if !c.supported("services.ports.mode") && p.Mode != "" {
|
||||
p.Mode = ""
|
||||
c.Unsupported("services.ports.mode")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPortsTarget(p *types.ServicePortConfig) {
|
||||
if !c.supported("services.ports.target") && p.Target != 0 {
|
||||
p.Target = 0
|
||||
c.Unsupported("services.ports.target")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPortsPublished(p *types.ServicePortConfig) {
|
||||
if !c.supported("services.ports.published") && p.Published != 0 {
|
||||
p.Published = 0
|
||||
c.Unsupported("services.ports.published")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPortsProtocol(p *types.ServicePortConfig) {
|
||||
if !c.supported("services.ports.protocol") && p.Protocol != "" {
|
||||
p.Protocol = ""
|
||||
c.Unsupported("services.ports.protocol")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPrivileged(service *types.ServiceConfig) {
|
||||
if !c.supported("services.privileged") && service.Privileged {
|
||||
service.Privileged = false
|
||||
c.Unsupported("services.privileged")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckPullPolicy(service *types.ServiceConfig) {
|
||||
if !c.supported("services.pull_policy") && service.PullPolicy != "" {
|
||||
service.PullPolicy = "false"
|
||||
c.Unsupported("services.pull_policy")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckReadOnly(service *types.ServiceConfig) {
|
||||
if !c.supported("services.read_only") && service.ReadOnly {
|
||||
service.ReadOnly = false
|
||||
c.Unsupported("services.read_only")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckRestart(service *types.ServiceConfig) {
|
||||
if !c.supported("services.restart") && service.Restart != "" {
|
||||
service.Restart = ""
|
||||
c.Unsupported("services.restart")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckRuntime(service *types.ServiceConfig) {
|
||||
if !c.supported("services.runtime") && service.Runtime != "" {
|
||||
service.Runtime = ""
|
||||
c.Unsupported("services.runtime")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckScale(service *types.ServiceConfig) {
|
||||
if !c.supported("services.scale") && service.Scale != 0 {
|
||||
service.Scale = 0
|
||||
c.Unsupported("services.scale")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckSecrets(service *types.ServiceConfig) {
|
||||
if len(service.Secrets) != 0 {
|
||||
if !c.supported("services.secrets") {
|
||||
service.Secrets = nil
|
||||
c.Unsupported("services.secrets")
|
||||
}
|
||||
for i, s := range service.Secrets {
|
||||
ref := types.FileReferenceConfig(s)
|
||||
c.CheckFileReference("services.secrets", &ref)
|
||||
service.Secrets[i] = s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileReference(s string, config *types.FileReferenceConfig) {
|
||||
c.CheckFileReferenceSource(s, config)
|
||||
c.CheckFileReferenceTarget(s, config)
|
||||
c.CheckFileReferenceGID(s, config)
|
||||
c.CheckFileReferenceUID(s, config)
|
||||
c.CheckFileReferenceMode(s, config)
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileReferenceSource(s string, config *types.FileReferenceConfig) {
|
||||
k := fmt.Sprintf("%s.source", s)
|
||||
if !c.supported(k) && config.Source != "" {
|
||||
config.Source = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileReferenceTarget(s string, config *types.FileReferenceConfig) {
|
||||
k := fmt.Sprintf("%s.target", s)
|
||||
if !c.supported(k) && config.Target == "" {
|
||||
config.Target = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileReferenceUID(s string, config *types.FileReferenceConfig) {
|
||||
k := fmt.Sprintf("%s.uid", s)
|
||||
if !c.supported(k) && config.UID != "" {
|
||||
config.UID = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileReferenceGID(s string, config *types.FileReferenceConfig) {
|
||||
k := fmt.Sprintf("%s.gid", s)
|
||||
if !c.supported(k) && config.GID != "" {
|
||||
config.GID = ""
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckFileReferenceMode(s string, config *types.FileReferenceConfig) {
|
||||
k := fmt.Sprintf("%s.mode", s)
|
||||
if !c.supported(k) && config.Mode != nil {
|
||||
config.Mode = nil
|
||||
c.Unsupported(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckSecurityOpt(service *types.ServiceConfig) {
|
||||
if !c.supported("services.security_opt") && len(service.SecurityOpt) != 0 {
|
||||
service.SecurityOpt = nil
|
||||
c.Unsupported("services.security_opt")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckShmSize(service *types.ServiceConfig) {
|
||||
if !c.supported("services.shm_size") && service.ShmSize != 0 {
|
||||
service.ShmSize = 0
|
||||
c.Unsupported("services.shm_size")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckStdinOpen(service *types.ServiceConfig) {
|
||||
if !c.supported("services.stdin_open") && service.StdinOpen {
|
||||
service.StdinOpen = true
|
||||
c.Unsupported("services.stdin_open")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckStopGracePeriod(service *types.ServiceConfig) {
|
||||
if !c.supported("services.stop_grace_period") && service.StopGracePeriod != nil {
|
||||
service.StopGracePeriod = nil
|
||||
c.Unsupported("services.stop_grace_period")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckStopSignal(service *types.ServiceConfig) {
|
||||
if !c.supported("services.stop_signal") && service.StopSignal != "" {
|
||||
service.StopSignal = ""
|
||||
c.Unsupported("services.stop_signal")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckSysctls(service *types.ServiceConfig) {
|
||||
if !c.supported("services.sysctls") && len(service.Sysctls) != 0 {
|
||||
service.Sysctls = nil
|
||||
c.Unsupported("services.sysctls")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckTmpfs(service *types.ServiceConfig) {
|
||||
if !c.supported("services.tmpfs") && len(service.Tmpfs) != 0 {
|
||||
service.Tmpfs = nil
|
||||
c.Unsupported("services.tmpfs")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckTty(service *types.ServiceConfig) {
|
||||
if !c.supported("services.tty") && service.Tty {
|
||||
service.Tty = false
|
||||
c.Unsupported("services.tty")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckUlimits(service *types.ServiceConfig) {
|
||||
if !c.supported("services.ulimits") && len(service.Ulimits) != 0 {
|
||||
service.Ulimits = nil
|
||||
c.Unsupported("services.ulimits")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckUser(service *types.ServiceConfig) {
|
||||
if !c.supported("services.user") && service.User != "" {
|
||||
service.User = ""
|
||||
c.Unsupported("services.user")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckUserNSMode(service *types.ServiceConfig) {
|
||||
if !c.supported("services.userns_mode") && service.UserNSMode != "" {
|
||||
service.UserNSMode = ""
|
||||
c.Unsupported("services.userns_mode")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckUts(service *types.ServiceConfig) {
|
||||
if !c.supported("services.build") && service.Uts != "" {
|
||||
service.Uts = ""
|
||||
c.Unsupported("services.uts")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumeDriver(service *types.ServiceConfig) {
|
||||
if !c.supported("services.volume_driver") && service.VolumeDriver != "" {
|
||||
service.VolumeDriver = ""
|
||||
c.Unsupported("services.volume_driver")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckServiceVolumes(service *types.ServiceConfig) bool {
|
||||
if !c.supported("services.volumes") {
|
||||
service.Volumes = nil
|
||||
c.Unsupported("services.volumes")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumesSource(config *types.ServiceVolumeConfig) {
|
||||
if !c.supported("services.volumes.source") && config.Source != "" {
|
||||
config.Source = ""
|
||||
c.Unsupported("services.volumes.source")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumesTarget(config *types.ServiceVolumeConfig) {
|
||||
if !c.supported("services.volumes.target") && config.Target != "" {
|
||||
config.Target = ""
|
||||
c.Unsupported("services.volumes.target")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumesReadOnly(config *types.ServiceVolumeConfig) {
|
||||
if !c.supported("services.volumes.read_only") && config.ReadOnly {
|
||||
config.ReadOnly = false
|
||||
c.Unsupported("services.volumes.read_only")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumesConsistency(config *types.ServiceVolumeConfig) {
|
||||
if !c.supported("services.volumes.consistency") && config.Consistency != "" {
|
||||
config.Consistency = ""
|
||||
c.Unsupported("services.volumes.consistency")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumesBind(config *types.ServiceVolumeBind) {
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
if !c.supported("services.volumes.bind.propagation") && config.Propagation != "" {
|
||||
config.Propagation = ""
|
||||
c.Unsupported("services.volumes.bind.propagation")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumesVolume(config *types.ServiceVolumeVolume) {
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
if !c.supported("services.volumes.nocopy") && config.NoCopy {
|
||||
config.NoCopy = false
|
||||
c.Unsupported("services.volumes.nocopy")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumesTmpfs(config *types.ServiceVolumeTmpfs) {
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
if !c.supported("services.volumes.tmpfs.size") && config.Size != 0 {
|
||||
config.Size = 0
|
||||
c.Unsupported("services.volumes.tmpfs.size")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumesFrom(service *types.ServiceConfig) {
|
||||
if !c.supported("services.volumes_from") && len(service.VolumesFrom) != 0 {
|
||||
service.VolumesFrom = nil
|
||||
c.Unsupported("services.volumes_from")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckWorkingDir(service *types.ServiceConfig) {
|
||||
if !c.supported("services.working_dir") && service.WorkingDir != "" {
|
||||
service.WorkingDir = ""
|
||||
c.Unsupported("services.working_dir")
|
||||
}
|
||||
}
|
47
pkg/third_party/compose-go/compatibility/volumes.go
vendored
Executable file
47
pkg/third_party/compose-go/compatibility/volumes.go
vendored
Executable file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compatibility
|
||||
|
||||
import "github.com/compose-spec/compose-go/types"
|
||||
|
||||
func (c *AllowList) CheckVolumeConfigDriver(config *types.VolumeConfig) {
|
||||
if !c.supported("volumes.driver") && config.Driver != "" {
|
||||
config.Driver = ""
|
||||
c.Unsupported("volumes.driver")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumeConfigDriverOpts(config *types.VolumeConfig) {
|
||||
if !c.supported("volumes.driver_opts") && len(config.DriverOpts) != 0 {
|
||||
config.DriverOpts = nil
|
||||
c.Unsupported("volumes.driver_opts")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumeConfigExternal(config *types.VolumeConfig) {
|
||||
if !c.supported("volumes.external") && config.External.External {
|
||||
config.External.External = false
|
||||
c.Unsupported("volumes.external")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AllowList) CheckVolumeConfigLabels(config *types.VolumeConfig) {
|
||||
if !c.supported("volumes.labels") && len(config.Labels) != 0 {
|
||||
config.Labels = nil
|
||||
c.Unsupported("volumes.labels")
|
||||
}
|
||||
}
|
53
pkg/third_party/compose-go/errdefs/errors.go
vendored
Executable file
53
pkg/third_party/compose-go/errdefs/errors.go
vendored
Executable file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package errdefs
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrNotFound is returned when an object is not found
|
||||
ErrNotFound = errors.New("not found")
|
||||
|
||||
// ErrInvalid is returned when a compose project is invalid
|
||||
ErrInvalid = errors.New("invalid compose project")
|
||||
|
||||
// ErrUnsupported is returned when a compose project uses an unsupported attribute
|
||||
ErrUnsupported = errors.New("unsupported attribute")
|
||||
|
||||
// ErrIncompatible is returned when a compose project uses an incompatible attribute
|
||||
ErrIncompatible = errors.New("incompatible attribute")
|
||||
)
|
||||
|
||||
// IsNotFoundError returns true if the unwrapped error is ErrNotFound
|
||||
func IsNotFoundError(err error) bool {
|
||||
return errors.Is(err, ErrNotFound)
|
||||
}
|
||||
|
||||
// IsInvalidError returns true if the unwrapped error is ErrInvalid
|
||||
func IsInvalidError(err error) bool {
|
||||
return errors.Is(err, ErrInvalid)
|
||||
}
|
||||
|
||||
// IsUnsupportedError returns true if the unwrapped error is ErrUnsupported
|
||||
func IsUnsupportedError(err error) bool {
|
||||
return errors.Is(err, ErrUnsupported)
|
||||
}
|
||||
|
||||
// IsUnsupportedError returns true if the unwrapped error is ErrIncompatible
|
||||
func IsIncompatibleError(err error) bool {
|
||||
return errors.Is(err, ErrIncompatible)
|
||||
}
|
22
pkg/third_party/compose-go/go.mod
vendored
Executable file
22
pkg/third_party/compose-go/go.mod
vendored
Executable file
@ -0,0 +1,22 @@
|
||||
module github.com/compose-spec/compose-go
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/docker/go-units v0.4.0
|
||||
github.com/google/go-cmp v0.5.5
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/mitchellh/mapstructure v1.4.1
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/ulyssessouza/godotenv v1.3.1-0.20210806120901-e417b721114e
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gotest.tools/v3 v3.0.3
|
||||
)
|
163
pkg/third_party/compose-go/go.sum
vendored
Executable file
163
pkg/third_party/compose-go/go.sum
vendored
Executable file
@ -0,0 +1,163 @@
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
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/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e h1:n81KvOMrLZa+VWHwST7dun9f0G98X3zREHS1ztYzZKU=
|
||||
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e/go.mod h1:xpWTC2KnJMiDLkoawhsPQcXjvwATEBcbq0xevG2YR9M=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
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/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
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/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/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/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/ulyssessouza/godotenv v1.3.1-0.20210806120901-e417b721114e h1:byEYm3QADv5mDUesYKstWwRodf2RoxxC/YuGOxtdqJw=
|
||||
github.com/ulyssessouza/godotenv v1.3.1-0.20210806120901-e417b721114e/go.mod h1:9JN/BuU6Agy5aHyEoA5EIPkBsYbk0+2R42zJgYi/SlI=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
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=
|
||||
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789 h1:NMiUjDZiD6qDVeBOzpImftxXzQHCp2Y2QLdmaqU9MRk=
|
||||
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
13
pkg/third_party/compose-go/golangci.yml
vendored
Executable file
13
pkg/third_party/compose-go/golangci.yml
vendored
Executable file
@ -0,0 +1,13 @@
|
||||
run:
|
||||
deadline: 2m
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
- golint
|
||||
- gosimple
|
||||
- ineffassign
|
||||
- misspell
|
||||
- govet
|
177
pkg/third_party/compose-go/interpolation/interpolation.go
vendored
Executable file
177
pkg/third_party/compose-go/interpolation/interpolation.go
vendored
Executable file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package interpolation
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/template"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Options supported by Interpolate
|
||||
type Options struct {
|
||||
// LookupValue from a key
|
||||
LookupValue LookupValue
|
||||
// TypeCastMapping maps key paths to functions to cast to a type
|
||||
TypeCastMapping map[Path]Cast
|
||||
// Substitution function to use
|
||||
Substitute func(string, template.Mapping) (string, error)
|
||||
}
|
||||
|
||||
// LookupValue is a function which maps from variable names to values.
|
||||
// Returns the value as a string and a bool indicating whether
|
||||
// the value is present, to distinguish between an empty string
|
||||
// and the absence of a value.
|
||||
type LookupValue func(key string) (string, bool)
|
||||
|
||||
// Cast a value to a new type, or return an error if the value can't be cast
|
||||
type Cast func(value string) (interface{}, error)
|
||||
|
||||
// Interpolate replaces variables in a string with the values from a mapping
|
||||
func Interpolate(config map[string]interface{}, opts Options) (map[string]interface{}, error) {
|
||||
if opts.LookupValue == nil {
|
||||
opts.LookupValue = os.LookupEnv
|
||||
}
|
||||
if opts.TypeCastMapping == nil {
|
||||
opts.TypeCastMapping = make(map[Path]Cast)
|
||||
}
|
||||
if opts.Substitute == nil {
|
||||
opts.Substitute = template.Substitute
|
||||
}
|
||||
|
||||
out := map[string]interface{}{}
|
||||
|
||||
for key, value := range config {
|
||||
interpolatedValue, err := recursiveInterpolate(value, NewPath(key), opts)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out[key] = interpolatedValue
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func recursiveInterpolate(value interface{}, path Path, opts Options) (interface{}, error) {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
newValue, err := opts.Substitute(value, template.Mapping(opts.LookupValue))
|
||||
if err != nil || newValue == value {
|
||||
return value, newPathError(path, err)
|
||||
}
|
||||
caster, ok := opts.getCasterForPath(path)
|
||||
if !ok {
|
||||
return newValue, nil
|
||||
}
|
||||
casted, err := caster(newValue)
|
||||
return casted, newPathError(path, errors.Wrap(err, "failed to cast to expected type"))
|
||||
|
||||
case map[string]interface{}:
|
||||
out := map[string]interface{}{}
|
||||
for key, elem := range value {
|
||||
interpolatedElem, err := recursiveInterpolate(elem, path.Next(key), opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[key] = interpolatedElem
|
||||
}
|
||||
return out, nil
|
||||
|
||||
case []interface{}:
|
||||
out := make([]interface{}, len(value))
|
||||
for i, elem := range value {
|
||||
interpolatedElem, err := recursiveInterpolate(elem, path.Next(PathMatchList), opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[i] = interpolatedElem
|
||||
}
|
||||
return out, nil
|
||||
|
||||
default:
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
func newPathError(path Path, err error) error {
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *template.InvalidTemplateError:
|
||||
return errors.Errorf(
|
||||
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
|
||||
path, err.Template)
|
||||
default:
|
||||
return errors.Wrapf(err, "error while interpolating %s", path)
|
||||
}
|
||||
}
|
||||
|
||||
const pathSeparator = "."
|
||||
|
||||
// PathMatchAll is a token used as part of a Path to match any key at that level
|
||||
// in the nested structure
|
||||
const PathMatchAll = "*"
|
||||
|
||||
// PathMatchList is a token used as part of a Path to match items in a list
|
||||
const PathMatchList = "[]"
|
||||
|
||||
// Path is a dotted path of keys to a value in a nested mapping structure. A *
|
||||
// section in a path will match any key in the mapping structure.
|
||||
type Path string
|
||||
|
||||
// NewPath returns a new Path
|
||||
func NewPath(items ...string) Path {
|
||||
return Path(strings.Join(items, pathSeparator))
|
||||
}
|
||||
|
||||
// Next returns a new path by append part to the current path
|
||||
func (p Path) Next(part string) Path {
|
||||
return Path(string(p) + pathSeparator + part)
|
||||
}
|
||||
|
||||
func (p Path) parts() []string {
|
||||
return strings.Split(string(p), pathSeparator)
|
||||
}
|
||||
|
||||
func (p Path) matches(pattern Path) bool {
|
||||
patternParts := pattern.parts()
|
||||
parts := p.parts()
|
||||
|
||||
if len(patternParts) != len(parts) {
|
||||
return false
|
||||
}
|
||||
for index, part := range parts {
|
||||
switch patternParts[index] {
|
||||
case PathMatchAll, part:
|
||||
continue
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (o Options) getCasterForPath(path Path) (Cast, bool) {
|
||||
for pattern, caster := range o.TypeCastMapping {
|
||||
if path.matches(pattern) {
|
||||
return caster, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
163
pkg/third_party/compose-go/interpolation/interpolation_test.go
vendored
Executable file
163
pkg/third_party/compose-go/interpolation/interpolation_test.go
vendored
Executable file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package interpolation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"strconv"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/env"
|
||||
)
|
||||
|
||||
var defaults = map[string]string{
|
||||
"USER": "jenny",
|
||||
"FOO": "bar",
|
||||
"count": "5",
|
||||
}
|
||||
|
||||
func defaultMapping(name string) (string, bool) {
|
||||
val, ok := defaults[name]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func TestInterpolate(t *testing.T) {
|
||||
services := map[string]interface{}{
|
||||
"servicea": map[string]interface{}{
|
||||
"image": "example:${USER}",
|
||||
"volumes": []interface{}{"$FOO:/target"},
|
||||
"logging": map[string]interface{}{
|
||||
"driver": "${FOO}",
|
||||
"options": map[string]interface{}{
|
||||
"user": "$USER",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
expected := map[string]interface{}{
|
||||
"servicea": map[string]interface{}{
|
||||
"image": "example:jenny",
|
||||
"volumes": []interface{}{"bar:/target"},
|
||||
"logging": map[string]interface{}{
|
||||
"driver": "bar",
|
||||
"options": map[string]interface{}{
|
||||
"user": "jenny",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := Interpolate(services, Options{LookupValue: defaultMapping})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, result))
|
||||
}
|
||||
|
||||
func TestInvalidInterpolation(t *testing.T) {
|
||||
services := map[string]interface{}{
|
||||
"servicea": map[string]interface{}{
|
||||
"image": "${",
|
||||
},
|
||||
}
|
||||
_, err := Interpolate(services, Options{LookupValue: defaultMapping})
|
||||
assert.Error(t, err, `invalid interpolation format for servicea.image: "${". You may need to escape any $ with another $.`)
|
||||
}
|
||||
|
||||
func TestInterpolateWithDefaults(t *testing.T) {
|
||||
defer env.Patch(t, "FOO", "BARZ")()
|
||||
|
||||
config := map[string]interface{}{
|
||||
"networks": map[string]interface{}{
|
||||
"foo": "thing_${FOO}",
|
||||
},
|
||||
}
|
||||
expected := map[string]interface{}{
|
||||
"networks": map[string]interface{}{
|
||||
"foo": "thing_BARZ",
|
||||
},
|
||||
}
|
||||
result, err := Interpolate(config, Options{})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, result))
|
||||
}
|
||||
|
||||
func TestInterpolateWithCast(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"replicas": "$count",
|
||||
},
|
||||
}
|
||||
toInt := func(value string) (interface{}, error) {
|
||||
return strconv.Atoi(value)
|
||||
}
|
||||
result, err := Interpolate(config, Options{
|
||||
LookupValue: defaultMapping,
|
||||
TypeCastMapping: map[Path]Cast{NewPath(PathMatchAll, "replicas"): toInt},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
expected := map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"replicas": 5,
|
||||
},
|
||||
}
|
||||
assert.Check(t, is.DeepEqual(expected, result))
|
||||
}
|
||||
|
||||
func TestPathMatches(t *testing.T) {
|
||||
var testcases = []struct {
|
||||
doc string
|
||||
path Path
|
||||
pattern Path
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
doc: "pattern too short",
|
||||
path: NewPath("one", "two", "three"),
|
||||
pattern: NewPath("one", "two"),
|
||||
},
|
||||
{
|
||||
doc: "pattern too long",
|
||||
path: NewPath("one", "two"),
|
||||
pattern: NewPath("one", "two", "three"),
|
||||
},
|
||||
{
|
||||
doc: "pattern mismatch",
|
||||
path: NewPath("one", "three", "two"),
|
||||
pattern: NewPath("one", "two", "three"),
|
||||
},
|
||||
{
|
||||
doc: "pattern mismatch with match-all part",
|
||||
path: NewPath("one", "three", "two"),
|
||||
pattern: NewPath(PathMatchAll, "two", "three"),
|
||||
},
|
||||
{
|
||||
doc: "pattern match with match-all part",
|
||||
path: NewPath("one", "two", "three"),
|
||||
pattern: NewPath("one", "*", "three"),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
doc: "pattern match",
|
||||
path: NewPath("one", "two", "three"),
|
||||
pattern: NewPath("one", "two", "three"),
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
assert.Check(t, is.Equal(testcase.expected, testcase.path.matches(testcase.pattern)))
|
||||
}
|
||||
}
|
8
pkg/third_party/compose-go/loader/example1.env
vendored
Executable file
8
pkg/third_party/compose-go/loader/example1.env
vendored
Executable file
@ -0,0 +1,8 @@
|
||||
# passed through
|
||||
FOO=foo_from_env_file
|
||||
|
||||
# overridden in example2.env
|
||||
BAR=bar_from_env_file
|
||||
|
||||
# overridden in full-example.yml
|
||||
BAZ=baz_from_env_file
|
4
pkg/third_party/compose-go/loader/example2.env
vendored
Executable file
4
pkg/third_party/compose-go/loader/example2.env
vendored
Executable file
@ -0,0 +1,4 @@
|
||||
BAR=bar_from_env_file_2
|
||||
|
||||
# overridden in configDetails.Environment
|
||||
QUX=quz_from_env_file_2
|
412
pkg/third_party/compose-go/loader/full-example.yml
vendored
Executable file
412
pkg/third_party/compose-go/loader/full-example.yml
vendored
Executable file
@ -0,0 +1,412 @@
|
||||
services:
|
||||
foo:
|
||||
|
||||
build:
|
||||
context: ./dir
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
foo: bar
|
||||
target: foo
|
||||
network: foo
|
||||
cache_from:
|
||||
- foo
|
||||
- bar
|
||||
labels: [FOO=BAR]
|
||||
|
||||
|
||||
cap_add:
|
||||
- ALL
|
||||
|
||||
cap_drop:
|
||||
- NET_ADMIN
|
||||
- SYS_ADMIN
|
||||
|
||||
cgroup_parent: m-executor-abcd
|
||||
|
||||
# String or list
|
||||
command: bundle exec thin -p 3000
|
||||
# command: ["bundle", "exec", "thin", "-p", "3000"]
|
||||
|
||||
configs:
|
||||
- config1
|
||||
- source: config2
|
||||
target: /my_config
|
||||
uid: '103'
|
||||
gid: '103'
|
||||
mode: 0440
|
||||
|
||||
container_name: my-web-container
|
||||
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 6
|
||||
labels: [FOO=BAR]
|
||||
rollback_config:
|
||||
parallelism: 3
|
||||
delay: 10s
|
||||
failure_action: continue
|
||||
monitor: 60s
|
||||
max_failure_ratio: 0.3
|
||||
order: start-first
|
||||
update_config:
|
||||
parallelism: 3
|
||||
delay: 10s
|
||||
failure_action: continue
|
||||
monitor: 60s
|
||||
max_failure_ratio: 0.3
|
||||
order: start-first
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.001'
|
||||
memory: 50M
|
||||
reservations:
|
||||
cpus: '0.0001'
|
||||
memory: 20M
|
||||
generic_resources:
|
||||
- discrete_resource_spec:
|
||||
kind: 'gpu'
|
||||
value: 2
|
||||
- discrete_resource_spec:
|
||||
kind: 'ssd'
|
||||
value: 1
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
placement:
|
||||
constraints: [node=foo]
|
||||
max_replicas_per_node: 5
|
||||
preferences:
|
||||
- spread: node.labels.az
|
||||
endpoint_mode: dnsrr
|
||||
|
||||
devices:
|
||||
- "/dev/ttyUSB0:/dev/ttyUSB0"
|
||||
|
||||
# String or list
|
||||
# dns: 8.8.8.8
|
||||
dns:
|
||||
- 8.8.8.8
|
||||
- 9.9.9.9
|
||||
|
||||
# String or list
|
||||
# dns_search: example.com
|
||||
dns_search:
|
||||
- dc1.example.com
|
||||
- dc2.example.com
|
||||
|
||||
domainname: foo.com
|
||||
|
||||
# String or list
|
||||
# entrypoint: /code/entrypoint.sh -p 3000
|
||||
entrypoint: ["/code/entrypoint.sh", "-p", "3000"]
|
||||
|
||||
# String or list
|
||||
# env_file: .env
|
||||
env_file:
|
||||
- ./example1.env
|
||||
- ./example2.env
|
||||
|
||||
# Mapping or list
|
||||
# Mapping values can be strings, numbers or null
|
||||
# Booleans are not allowed - must be quoted
|
||||
environment:
|
||||
BAZ: baz_from_service_def
|
||||
QUX:
|
||||
# environment:
|
||||
# - RACK_ENV=development
|
||||
# - SHOW=true
|
||||
# - SESSION_SECRET
|
||||
|
||||
# Items can be strings or numbers
|
||||
expose:
|
||||
- "3000"
|
||||
- 8000
|
||||
|
||||
external_links:
|
||||
- redis_1
|
||||
- project_db_1:mysql
|
||||
- project_db_1:postgresql
|
||||
|
||||
# Mapping or list
|
||||
# Mapping values must be strings
|
||||
# extra_hosts:
|
||||
# somehost: "162.242.195.82"
|
||||
# otherhost: "50.31.209.229"
|
||||
extra_hosts:
|
||||
- "somehost:162.242.195.82"
|
||||
- "otherhost:50.31.209.229"
|
||||
|
||||
hostname: foo
|
||||
|
||||
healthcheck:
|
||||
test: echo "hello world"
|
||||
interval: 10s
|
||||
timeout: 1s
|
||||
retries: 5
|
||||
start_period: 15s
|
||||
|
||||
# Any valid image reference - repo, tag, id, sha
|
||||
image: redis
|
||||
# image: ubuntu:14.04
|
||||
# image: tutum/influxdb
|
||||
# image: example-registry.com:4000/postgresql
|
||||
# image: a4bc65fd
|
||||
# image: busybox@sha256:38a203e1986cf79639cfb9b2e1d6e773de84002feea2d4eb006b52004ee8502d
|
||||
|
||||
ipc: host
|
||||
|
||||
# Mapping or list
|
||||
# Mapping values can be strings, numbers or null
|
||||
labels:
|
||||
com.example.description: "Accounting webapp"
|
||||
com.example.number: 42
|
||||
com.example.empty-label:
|
||||
# labels:
|
||||
# - "com.example.description=Accounting webapp"
|
||||
# - "com.example.number=42"
|
||||
# - "com.example.empty-label"
|
||||
|
||||
links:
|
||||
- db
|
||||
- db:database
|
||||
- redis
|
||||
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
syslog-address: "tcp://192.168.0.42:123"
|
||||
|
||||
mac_address: 02:42:ac:11:65:43
|
||||
|
||||
# network_mode: "bridge"
|
||||
# network_mode: "host"
|
||||
# network_mode: "none"
|
||||
# Use the network mode of an arbitrary container from another service
|
||||
# network_mode: "service:db"
|
||||
# Use the network mode of another container, specified by name or id
|
||||
# network_mode: "container:some-container"
|
||||
network_mode: "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b"
|
||||
|
||||
networks:
|
||||
some-network:
|
||||
aliases:
|
||||
- alias1
|
||||
- alias3
|
||||
other-network:
|
||||
ipv4_address: 172.16.238.10
|
||||
ipv6_address: 2001:3984:3989::10
|
||||
other-other-network:
|
||||
|
||||
pid: "host"
|
||||
|
||||
ports:
|
||||
- 3000
|
||||
- "3001-3005"
|
||||
- "8000:8000"
|
||||
- "9090-9091:8080-8081"
|
||||
- "49100:22"
|
||||
- "127.0.0.1:8001:8001"
|
||||
- "127.0.0.1:5000-5010:5000-5010"
|
||||
|
||||
privileged: true
|
||||
|
||||
read_only: true
|
||||
|
||||
restart: always
|
||||
|
||||
secrets:
|
||||
- secret1
|
||||
- source: secret2
|
||||
target: my_secret
|
||||
uid: '103'
|
||||
gid: '103'
|
||||
mode: 0440
|
||||
|
||||
security_opt:
|
||||
- label=level:s0:c100,c200
|
||||
- label=type:svirt_apache_t
|
||||
|
||||
stdin_open: true
|
||||
|
||||
stop_grace_period: 20s
|
||||
|
||||
stop_signal: SIGUSR1
|
||||
|
||||
sysctls:
|
||||
net.core.somaxconn: 1024
|
||||
net.ipv4.tcp_syncookies: 0
|
||||
|
||||
# String or list
|
||||
# tmpfs: /run
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
||||
|
||||
tty: true
|
||||
|
||||
ulimits:
|
||||
# Single number or mapping with soft + hard limits
|
||||
nproc: 65535
|
||||
nofile:
|
||||
soft: 20000
|
||||
hard: 40000
|
||||
|
||||
user: someone
|
||||
|
||||
volumes:
|
||||
# Just specify a path and let the Engine create a volume
|
||||
- /var/lib/mysql
|
||||
# Specify an absolute path mapping
|
||||
- /opt/data:/var/lib/mysql
|
||||
# Path on the host, relative to the Compose file
|
||||
- .:/code
|
||||
- ./static:/var/www/html
|
||||
# User-relative path
|
||||
- ~/configs:/etc/configs/:ro
|
||||
# Named volume
|
||||
- datavolume:/var/lib/mysql
|
||||
- type: bind
|
||||
source: ./opt
|
||||
target: /opt
|
||||
consistency: cached
|
||||
- type: tmpfs
|
||||
target: /opt
|
||||
tmpfs:
|
||||
size: 10000
|
||||
|
||||
working_dir: /code
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
|
||||
networks:
|
||||
# Entries can be null, which specifies simply that a network
|
||||
# called "{project name}_some-network" should be created and
|
||||
# use the default driver
|
||||
some-network:
|
||||
|
||||
other-network:
|
||||
driver: overlay
|
||||
|
||||
driver_opts:
|
||||
# Values can be strings or numbers
|
||||
foo: "bar"
|
||||
baz: 1
|
||||
|
||||
ipam:
|
||||
driver: overlay
|
||||
# driver_opts:
|
||||
# # Values can be strings or numbers
|
||||
# com.docker.network.enable_ipv6: "true"
|
||||
# com.docker.network.numeric_value: 1
|
||||
config:
|
||||
- subnet: 172.28.0.0/16
|
||||
ip_range: 172.28.5.0/24
|
||||
gateway: 172.28.5.254
|
||||
aux_addresses:
|
||||
host1: 172.28.1.5
|
||||
host2: 172.28.1.6
|
||||
host3: 172.28.1.7
|
||||
- subnet: 2001:3984:3989::/64
|
||||
gateway: 2001:3984:3989::1
|
||||
|
||||
labels:
|
||||
foo: bar
|
||||
|
||||
external-network:
|
||||
# Specifies that a pre-existing network called "external-network"
|
||||
# can be referred to within this file as "external-network"
|
||||
external: true
|
||||
|
||||
other-external-network:
|
||||
# Specifies that a pre-existing network called "my-cool-network"
|
||||
# can be referred to within this file as "other-external-network"
|
||||
external:
|
||||
name: my-cool-network
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
|
||||
volumes:
|
||||
# Entries can be null, which specifies simply that a volume
|
||||
# called "{project name}_some-volume" should be created and
|
||||
# use the default driver
|
||||
some-volume:
|
||||
|
||||
other-volume:
|
||||
driver: flocker
|
||||
|
||||
driver_opts:
|
||||
# Values can be strings or numbers
|
||||
foo: "bar"
|
||||
baz: 1
|
||||
labels:
|
||||
foo: bar
|
||||
|
||||
another-volume:
|
||||
name: "user_specified_name"
|
||||
driver: vsphere
|
||||
|
||||
driver_opts:
|
||||
# Values can be strings or numbers
|
||||
foo: "bar"
|
||||
baz: 1
|
||||
|
||||
external-volume:
|
||||
# Specifies that a pre-existing volume called "external-volume"
|
||||
# can be referred to within this file as "external-volume"
|
||||
external: true
|
||||
|
||||
other-external-volume:
|
||||
# Specifies that a pre-existing volume called "my-cool-volume"
|
||||
# can be referred to within this file as "other-external-volume"
|
||||
# This example uses the deprecated "volume.external.name" (replaced by "volume.name")
|
||||
external:
|
||||
name: my-cool-volume
|
||||
|
||||
external-volume3:
|
||||
# Specifies that a pre-existing volume called "this-is-volume3"
|
||||
# can be referred to within this file as "external-volume3"
|
||||
name: this-is-volume3
|
||||
external: true
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
|
||||
configs:
|
||||
config1:
|
||||
file: ./config_data
|
||||
labels:
|
||||
foo: bar
|
||||
config2:
|
||||
external:
|
||||
name: my_config
|
||||
config3:
|
||||
external: true
|
||||
config4:
|
||||
name: foo
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
|
||||
secrets:
|
||||
secret1:
|
||||
file: ./secret_data
|
||||
labels:
|
||||
foo: bar
|
||||
secret2:
|
||||
external:
|
||||
name: my_secret
|
||||
secret3:
|
||||
external: true
|
||||
secret4:
|
||||
name: bar
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
x-nested:
|
||||
bar: baz
|
||||
foo: bar
|
1569
pkg/third_party/compose-go/loader/full-struct_test.go
vendored
Executable file
1569
pkg/third_party/compose-go/loader/full-struct_test.go
vendored
Executable file
File diff suppressed because it is too large
Load Diff
132
pkg/third_party/compose-go/loader/interpolate.go
vendored
Executable file
132
pkg/third_party/compose-go/loader/interpolate.go
vendored
Executable file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
interp "github.com/compose-spec/compose-go/interpolation"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var interpolateTypeCastMapping = map[interp.Path]interp.Cast{
|
||||
servicePath("configs", interp.PathMatchList, "mode"): toInt,
|
||||
servicePath("cpu_count"): toInt64,
|
||||
servicePath("cpu_percent"): toFloat,
|
||||
servicePath("cpu_period"): toInt64,
|
||||
servicePath("cpu_quota"): toInt64,
|
||||
servicePath("cpu_rt_period"): toInt64,
|
||||
servicePath("cpu_rt_runtime"): toInt64,
|
||||
servicePath("cpus"): toFloat32,
|
||||
servicePath("cpu_shares"): toInt64,
|
||||
servicePath("init"): toBoolean,
|
||||
servicePath("deploy", "replicas"): toInt,
|
||||
servicePath("deploy", "update_config", "parallelism"): toInt,
|
||||
servicePath("deploy", "update_config", "max_failure_ratio"): toFloat,
|
||||
servicePath("deploy", "rollback_config", "parallelism"): toInt,
|
||||
servicePath("deploy", "rollback_config", "max_failure_ratio"): toFloat,
|
||||
servicePath("deploy", "restart_policy", "max_attempts"): toInt,
|
||||
servicePath("deploy", "placement", "max_replicas_per_node"): toInt,
|
||||
servicePath("healthcheck", "retries"): toInt,
|
||||
servicePath("healthcheck", "disable"): toBoolean,
|
||||
servicePath("mem_limit"): toUnitBytes,
|
||||
servicePath("mem_reservation"): toUnitBytes,
|
||||
servicePath("memswap_limit"): toUnitBytes,
|
||||
servicePath("mem_swappiness"): toUnitBytes,
|
||||
servicePath("oom_kill_disable"): toBoolean,
|
||||
servicePath("oom_score_adj"): toInt64,
|
||||
servicePath("pids_limit"): toInt64,
|
||||
servicePath("ports", interp.PathMatchList, "target"): toInt,
|
||||
servicePath("ports", interp.PathMatchList, "published"): toInt,
|
||||
servicePath("privileged"): toBoolean,
|
||||
servicePath("read_only"): toBoolean,
|
||||
servicePath("scale"): toInt,
|
||||
servicePath("secrets", interp.PathMatchList, "mode"): toInt,
|
||||
servicePath("shm_size"): toUnitBytes,
|
||||
servicePath("stdin_open"): toBoolean,
|
||||
servicePath("stop_grace_period"): toDuration,
|
||||
servicePath("tty"): toBoolean,
|
||||
servicePath("ulimits", interp.PathMatchAll): toInt,
|
||||
servicePath("ulimits", interp.PathMatchAll, "hard"): toInt,
|
||||
servicePath("ulimits", interp.PathMatchAll, "soft"): toInt,
|
||||
servicePath("volumes", interp.PathMatchList, "read_only"): toBoolean,
|
||||
servicePath("volumes", interp.PathMatchList, "volume", "nocopy"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "internal"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "attachable"): toBoolean,
|
||||
iPath("volumes", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("secrets", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("configs", interp.PathMatchAll, "external"): toBoolean,
|
||||
}
|
||||
|
||||
func iPath(parts ...string) interp.Path {
|
||||
return interp.NewPath(parts...)
|
||||
}
|
||||
|
||||
func servicePath(parts ...string) interp.Path {
|
||||
return iPath(append([]string{"services", interp.PathMatchAll}, parts...)...)
|
||||
}
|
||||
|
||||
func toInt(value string) (interface{}, error) {
|
||||
return strconv.Atoi(value)
|
||||
}
|
||||
|
||||
func toInt64(value string) (interface{}, error) {
|
||||
return strconv.ParseInt(value, 10, 64)
|
||||
}
|
||||
|
||||
func toUnitBytes(value string) (interface{}, error) {
|
||||
i, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return types.UnitBytes(i), nil
|
||||
}
|
||||
|
||||
func toDuration(value string) (interface{}, error) {
|
||||
i, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return types.Duration(i), nil
|
||||
}
|
||||
|
||||
func toFloat(value string) (interface{}, error) {
|
||||
return strconv.ParseFloat(value, 64)
|
||||
}
|
||||
|
||||
func toFloat32(value string) (interface{}, error) {
|
||||
f, err := strconv.ParseFloat(value, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return float32(f), nil
|
||||
}
|
||||
|
||||
// should match http://yaml.org/type/bool.html
|
||||
func toBoolean(value string) (interface{}, error) {
|
||||
switch strings.ToLower(value) {
|
||||
case "y", "yes", "true", "on":
|
||||
return true, nil
|
||||
case "n", "no", "false", "off":
|
||||
return false, nil
|
||||
default:
|
||||
return nil, errors.Errorf("invalid boolean: %s", value)
|
||||
}
|
||||
}
|
1062
pkg/third_party/compose-go/loader/loader.go
vendored
Executable file
1062
pkg/third_party/compose-go/loader/loader.go
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1809
pkg/third_party/compose-go/loader/loader_test.go
vendored
Executable file
1809
pkg/third_party/compose-go/loader/loader_test.go
vendored
Executable file
File diff suppressed because it is too large
Load Diff
349
pkg/third_party/compose-go/loader/merge.go
vendored
Executable file
349
pkg/third_party/compose-go/loader/merge.go
vendored
Executable file
@ -0,0 +1,349 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type specials struct {
|
||||
m map[reflect.Type]func(dst, src reflect.Value) error
|
||||
}
|
||||
|
||||
var serviceSpecials = &specials{
|
||||
m: map[reflect.Type]func(dst, src reflect.Value) error{
|
||||
reflect.TypeOf(&types.LoggingConfig{}): safelyMerge(mergeLoggingConfig),
|
||||
reflect.TypeOf(&types.UlimitsConfig{}): safelyMerge(mergeUlimitsConfig),
|
||||
reflect.TypeOf([]types.ServiceVolumeConfig{}): mergeSlice(toServiceVolumeConfigsMap, toServiceVolumeConfigsSlice),
|
||||
reflect.TypeOf([]types.ServicePortConfig{}): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice),
|
||||
reflect.TypeOf([]types.ServiceSecretConfig{}): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice),
|
||||
reflect.TypeOf([]types.ServiceConfigObjConfig{}): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice),
|
||||
reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig,
|
||||
reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig,
|
||||
},
|
||||
}
|
||||
|
||||
func (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) error {
|
||||
if fn, ok := s.m[t]; ok {
|
||||
return fn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func merge(configs []*types.Config) (*types.Config, error) {
|
||||
base := configs[0]
|
||||
for _, override := range configs[1:] {
|
||||
var err error
|
||||
base.Services, err = mergeServices(base.Services, override.Services)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge services from %s", override.Filename)
|
||||
}
|
||||
base.Volumes, err = mergeVolumes(base.Volumes, override.Volumes)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge volumes from %s", override.Filename)
|
||||
}
|
||||
base.Networks, err = mergeNetworks(base.Networks, override.Networks)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge networks from %s", override.Filename)
|
||||
}
|
||||
base.Secrets, err = mergeSecrets(base.Secrets, override.Secrets)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge secrets from %s", override.Filename)
|
||||
}
|
||||
base.Configs, err = mergeConfigs(base.Configs, override.Configs)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge configs from %s", override.Filename)
|
||||
}
|
||||
base.Extensions, err = mergeExtensions(base.Extensions, override.Extensions)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge extensions from %s", override.Filename)
|
||||
}
|
||||
}
|
||||
return base, nil
|
||||
}
|
||||
|
||||
func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) {
|
||||
baseServices := mapByName(base)
|
||||
overrideServices := mapByName(override)
|
||||
for name, overrideService := range overrideServices {
|
||||
overrideService := overrideService
|
||||
if baseService, ok := baseServices[name]; ok {
|
||||
merged, err := _merge(&baseService, &overrideService)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "cannot merge service %s", name)
|
||||
}
|
||||
baseServices[name] = *merged
|
||||
continue
|
||||
}
|
||||
baseServices[name] = overrideService
|
||||
}
|
||||
services := []types.ServiceConfig{}
|
||||
for _, baseService := range baseServices {
|
||||
services = append(services, baseService)
|
||||
}
|
||||
sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name })
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func _merge(baseService *types.ServiceConfig, overrideService *types.ServiceConfig) (*types.ServiceConfig, error) {
|
||||
if err := mergo.Merge(baseService, overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(serviceSpecials)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if overrideService.Command != nil {
|
||||
baseService.Command = overrideService.Command
|
||||
}
|
||||
if overrideService.Entrypoint != nil {
|
||||
baseService.Entrypoint = overrideService.Entrypoint
|
||||
}
|
||||
return baseService, nil
|
||||
}
|
||||
|
||||
func toServiceSecretConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
|
||||
secrets, ok := s.([]types.ServiceSecretConfig)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("not a serviceSecretConfig: %v", s)
|
||||
}
|
||||
m := map[interface{}]interface{}{}
|
||||
for _, secret := range secrets {
|
||||
m[secret.Source] = secret
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func toServiceConfigObjConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
|
||||
secrets, ok := s.([]types.ServiceConfigObjConfig)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("not a serviceSecretConfig: %v", s)
|
||||
}
|
||||
m := map[interface{}]interface{}{}
|
||||
for _, secret := range secrets {
|
||||
m[secret.Source] = secret
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func toServicePortConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
|
||||
ports, ok := s.([]types.ServicePortConfig)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("not a servicePortConfig slice: %v", s)
|
||||
}
|
||||
m := map[interface{}]interface{}{}
|
||||
type port struct {
|
||||
target uint32
|
||||
published uint32
|
||||
ip string
|
||||
protocol string
|
||||
}
|
||||
|
||||
for _, p := range ports {
|
||||
mergeKey := port{
|
||||
target: p.Target,
|
||||
published: p.Published,
|
||||
ip: p.HostIP,
|
||||
protocol: p.Protocol,
|
||||
}
|
||||
m[mergeKey] = p
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func toServiceVolumeConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
|
||||
volumes, ok := s.([]types.ServiceVolumeConfig)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("not a ServiceVolumeConfig slice: %v", s)
|
||||
}
|
||||
m := map[interface{}]interface{}{}
|
||||
for _, v := range volumes {
|
||||
m[v.Target] = v
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func toServiceSecretConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error {
|
||||
s := []types.ServiceSecretConfig{}
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceSecretConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source })
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error {
|
||||
s := []types.ServiceConfigObjConfig{}
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceConfigObjConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source })
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
func toServicePortConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error {
|
||||
s := []types.ServicePortConfig{}
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServicePortConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool {
|
||||
if s[i].Target != s[j].Target {
|
||||
return s[i].Target < s[j].Target
|
||||
}
|
||||
if s[i].Published != s[j].Published {
|
||||
return s[i].Published < s[j].Published
|
||||
}
|
||||
if s[i].HostIP != s[j].HostIP {
|
||||
return s[i].HostIP < s[j].HostIP
|
||||
}
|
||||
return s[i].Protocol < s[j].Protocol
|
||||
})
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
func toServiceVolumeConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error {
|
||||
s := []types.ServiceVolumeConfig{}
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceVolumeConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Target < s[j].Target })
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
type tomapFn func(s interface{}) (map[interface{}]interface{}, error)
|
||||
type writeValueFromMapFn func(reflect.Value, map[interface{}]interface{}) error
|
||||
|
||||
func safelyMerge(mergeFn func(dst, src reflect.Value) error) func(dst, src reflect.Value) error {
|
||||
return func(dst, src reflect.Value) error {
|
||||
if src.IsNil() {
|
||||
return nil
|
||||
}
|
||||
if dst.IsNil() {
|
||||
dst.Set(src)
|
||||
return nil
|
||||
}
|
||||
return mergeFn(dst, src)
|
||||
}
|
||||
}
|
||||
|
||||
func mergeSlice(tomap tomapFn, writeValue writeValueFromMapFn) func(dst, src reflect.Value) error {
|
||||
return func(dst, src reflect.Value) error {
|
||||
dstMap, err := sliceToMap(tomap, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcMap, err := sliceToMap(tomap, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mergo.Map(&dstMap, srcMap, mergo.WithOverride); err != nil {
|
||||
return err
|
||||
}
|
||||
return writeValue(dst, dstMap)
|
||||
}
|
||||
}
|
||||
|
||||
func sliceToMap(tomap tomapFn, v reflect.Value) (map[interface{}]interface{}, error) {
|
||||
// check if valid
|
||||
if !v.IsValid() {
|
||||
return nil, errors.Errorf("invalid value : %+v", v)
|
||||
}
|
||||
return tomap(v.Interface())
|
||||
}
|
||||
|
||||
func mergeLoggingConfig(dst, src reflect.Value) error {
|
||||
// Same driver, merging options
|
||||
if getLoggingDriver(dst.Elem()) == getLoggingDriver(src.Elem()) ||
|
||||
getLoggingDriver(dst.Elem()) == "" || getLoggingDriver(src.Elem()) == "" {
|
||||
if getLoggingDriver(dst.Elem()) == "" {
|
||||
dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem()))
|
||||
}
|
||||
dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string)
|
||||
srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string)
|
||||
return mergo.Merge(&dstOptions, srcOptions, mergo.WithOverride)
|
||||
}
|
||||
// Different driver, override with src
|
||||
dst.Set(src)
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint: unparam
|
||||
func mergeUlimitsConfig(dst, src reflect.Value) error {
|
||||
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
|
||||
dst.Elem().Set(src.Elem())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint: unparam
|
||||
func mergeServiceNetworkConfig(dst, src reflect.Value) error {
|
||||
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
|
||||
dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases"))
|
||||
if ipv4 := src.Elem().FieldByName("Ipv4Address").Interface().(string); ipv4 != "" {
|
||||
dst.Elem().FieldByName("Ipv4Address").SetString(ipv4)
|
||||
}
|
||||
if ipv6 := src.Elem().FieldByName("Ipv6Address").Interface().(string); ipv6 != "" {
|
||||
dst.Elem().FieldByName("Ipv6Address").SetString(ipv6)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLoggingDriver(v reflect.Value) string {
|
||||
return v.FieldByName("Driver").String()
|
||||
}
|
||||
|
||||
func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig {
|
||||
m := map[string]types.ServiceConfig{}
|
||||
for _, service := range services {
|
||||
m[service.Name] = service
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeExtensions(base, override map[string]interface{}) (map[string]interface{}, error) {
|
||||
if base == nil {
|
||||
base = map[string]interface{}{}
|
||||
}
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
1226
pkg/third_party/compose-go/loader/merge_test.go
vendored
Executable file
1226
pkg/third_party/compose-go/loader/merge_test.go
vendored
Executable file
File diff suppressed because it is too large
Load Diff
261
pkg/third_party/compose-go/loader/normalize.go
vendored
Executable file
261
pkg/third_party/compose-go/loader/normalize.go
vendored
Executable file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/compose-spec/compose-go/errdefs"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// normalize compose project by moving deprecated attributes to their canonical position and injecting implicit defaults
|
||||
func normalize(project *types.Project, resolvePaths bool) error {
|
||||
absWorkingDir, err := filepath.Abs(project.WorkingDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
project.WorkingDir = absWorkingDir
|
||||
|
||||
absComposeFiles, err := absComposeFiles(project.ComposeFiles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
project.ComposeFiles = absComposeFiles
|
||||
|
||||
// If not declared explicitly, Compose model involves an implicit "default" network
|
||||
if _, ok := project.Networks["default"]; !ok {
|
||||
project.Networks["default"] = types.NetworkConfig{}
|
||||
}
|
||||
|
||||
err = relocateExternalName(project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, s := range project.Services {
|
||||
if len(s.Networks) == 0 && s.NetworkMode == "" {
|
||||
// Service without explicit network attachment are implicitly exposed on default network
|
||||
s.Networks = map[string]*types.ServiceNetworkConfig{"default": nil}
|
||||
}
|
||||
|
||||
if s.PullPolicy == types.PullPolicyIfNotPresent {
|
||||
s.PullPolicy = types.PullPolicyMissing
|
||||
}
|
||||
|
||||
fn := func(s string) (string, bool) {
|
||||
v, ok := project.Environment[s]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
if s.Build != nil {
|
||||
if s.Build.Dockerfile == "" {
|
||||
s.Build.Dockerfile = "Dockerfile"
|
||||
}
|
||||
localContext := absPath(project.WorkingDir, s.Build.Context)
|
||||
if _, err := os.Stat(localContext); err == nil {
|
||||
if resolvePaths {
|
||||
s.Build.Context = localContext
|
||||
s.Build.Dockerfile = absPath(localContext, s.Build.Dockerfile)
|
||||
}
|
||||
} else {
|
||||
// might be a remote http/git context. Unfortunately supported "remote" syntax is highly ambiguous
|
||||
// in moby/moby and not defined by compose-spec, so let's assume runtime will check
|
||||
}
|
||||
s.Build.Args = s.Build.Args.Resolve(fn)
|
||||
}
|
||||
s.Environment = s.Environment.Resolve(fn)
|
||||
|
||||
err := relocateLogDriver(&s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = relocateLogOpt(&s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = relocateDockerfile(&s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = relocateScale(&s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project.Services[i] = s
|
||||
}
|
||||
|
||||
setNameFromKey(project)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func relocateScale(s *types.ServiceConfig) error {
|
||||
scale := uint64(s.Scale)
|
||||
if scale != 1 {
|
||||
logrus.Warn("`scale` is deprecated. Use the `deploy.replicas` element")
|
||||
if s.Deploy == nil {
|
||||
s.Deploy = &types.DeployConfig{}
|
||||
}
|
||||
if s.Deploy.Replicas != nil && *s.Deploy.Replicas != scale {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'scale' (deprecated) and 'deploy.replicas'")
|
||||
}
|
||||
s.Deploy.Replicas = &scale
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func absComposeFiles(composeFiles []string) ([]string, error) {
|
||||
absComposeFiles := make([]string, len(composeFiles))
|
||||
for i, composeFile := range composeFiles {
|
||||
absComposefile, err := filepath.Abs(composeFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
absComposeFiles[i] = absComposefile
|
||||
}
|
||||
return absComposeFiles, nil
|
||||
}
|
||||
|
||||
// Resources with no explicit name are actually named by their key in map
|
||||
func setNameFromKey(project *types.Project) {
|
||||
for i, n := range project.Networks {
|
||||
if n.Name == "" {
|
||||
n.Name = fmt.Sprintf("%s_%s", project.Name, i)
|
||||
project.Networks[i] = n
|
||||
}
|
||||
}
|
||||
|
||||
for i, v := range project.Volumes {
|
||||
if v.Name == "" {
|
||||
v.Name = fmt.Sprintf("%s_%s", project.Name, i)
|
||||
project.Volumes[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
for i, c := range project.Configs {
|
||||
if c.Name == "" {
|
||||
c.Name = fmt.Sprintf("%s_%s", project.Name, i)
|
||||
project.Configs[i] = c
|
||||
}
|
||||
}
|
||||
|
||||
for i, s := range project.Secrets {
|
||||
if s.Name == "" {
|
||||
s.Name = fmt.Sprintf("%s_%s", project.Name, i)
|
||||
project.Secrets[i] = s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func relocateExternalName(project *types.Project) error {
|
||||
for i, n := range project.Networks {
|
||||
if n.External.Name != "" {
|
||||
if n.Name != "" {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'networks.external.name' (deprecated) and 'networks.name'")
|
||||
}
|
||||
n.Name = n.External.Name
|
||||
}
|
||||
project.Networks[i] = n
|
||||
}
|
||||
|
||||
for i, v := range project.Volumes {
|
||||
if v.External.Name != "" {
|
||||
if v.Name != "" {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'volumes.external.name' (deprecated) and 'volumes.name'")
|
||||
}
|
||||
v.Name = v.External.Name
|
||||
}
|
||||
project.Volumes[i] = v
|
||||
}
|
||||
|
||||
for i, s := range project.Secrets {
|
||||
if s.External.Name != "" {
|
||||
if s.Name != "" {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'secrets.external.name' (deprecated) and 'secrets.name'")
|
||||
}
|
||||
s.Name = s.External.Name
|
||||
}
|
||||
project.Secrets[i] = s
|
||||
}
|
||||
|
||||
for i, c := range project.Configs {
|
||||
if c.External.Name != "" {
|
||||
if c.Name != "" {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'configs.external.name' (deprecated) and 'configs.name'")
|
||||
}
|
||||
c.Name = c.External.Name
|
||||
}
|
||||
project.Configs[i] = c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func relocateLogOpt(s *types.ServiceConfig) error {
|
||||
if len(s.LogOpt) != 0 {
|
||||
logrus.Warn("`log_opts` is deprecated. Use the `logging` element")
|
||||
if s.Logging == nil {
|
||||
s.Logging = &types.LoggingConfig{}
|
||||
}
|
||||
for k, v := range s.LogOpt {
|
||||
if _, ok := s.Logging.Options[k]; !ok {
|
||||
s.Logging.Options[k] = v
|
||||
} else {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'log_opt' (deprecated) and 'logging.options'")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func relocateLogDriver(s *types.ServiceConfig) error {
|
||||
if s.LogDriver != "" {
|
||||
logrus.Warn("`log_driver` is deprecated. Use the `logging` element")
|
||||
if s.Logging == nil {
|
||||
s.Logging = &types.LoggingConfig{}
|
||||
}
|
||||
if s.Logging.Driver == "" {
|
||||
s.Logging.Driver = s.LogDriver
|
||||
} else {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'log_driver' (deprecated) and 'logging.driver'")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func relocateDockerfile(s *types.ServiceConfig) error {
|
||||
if s.Dockerfile != "" {
|
||||
logrus.Warn("`dockerfile` is deprecated. Use the `build` element")
|
||||
if s.Build == nil {
|
||||
s.Build = &types.BuildConfig{}
|
||||
}
|
||||
if s.Dockerfile == "" {
|
||||
s.Build.Dockerfile = s.Dockerfile
|
||||
} else {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'dockerfile' (deprecated) and 'build.dockerfile'")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
148
pkg/third_party/compose-go/loader/normalize_test.go
vendored
Executable file
148
pkg/third_party/compose-go/loader/normalize_test.go
vendored
Executable file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestNormalizeNetworkNames(t *testing.T) {
|
||||
wd, _ := os.Getwd()
|
||||
project := types.Project{
|
||||
Name: "myProject",
|
||||
WorkingDir: wd,
|
||||
Environment: map[string]string{
|
||||
"FOO": "BAR",
|
||||
},
|
||||
Networks: types.Networks{
|
||||
"myExternalnet": {
|
||||
Name: "myExternalnet", // this is automaticaly setup by loader for externa networks before reaching normalization
|
||||
External: types.External{External: true},
|
||||
},
|
||||
"mynet": {},
|
||||
"myNamedNet": {
|
||||
Name: "CustomName",
|
||||
},
|
||||
},
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Name: "foo",
|
||||
Build: &types.BuildConfig{
|
||||
Context: "./testdata",
|
||||
Args: map[string]*string{
|
||||
"FOO": nil,
|
||||
"ZOT": nil,
|
||||
},
|
||||
},
|
||||
Scale: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expected := `services:
|
||||
foo:
|
||||
build:
|
||||
context: ./testdata
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
FOO: BAR
|
||||
ZOT: null
|
||||
networks:
|
||||
default: null
|
||||
networks:
|
||||
default:
|
||||
name: myProject_default
|
||||
myExternalnet:
|
||||
name: myExternalnet
|
||||
external: true
|
||||
myNamedNet:
|
||||
name: CustomName
|
||||
mynet:
|
||||
name: myProject_mynet
|
||||
`
|
||||
err := normalize(&project, false)
|
||||
assert.NilError(t, err)
|
||||
marshal, err := yaml.Marshal(project)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, expected, string(marshal))
|
||||
}
|
||||
|
||||
func TestNormalizeAbsolutePaths(t *testing.T) {
|
||||
project := types.Project{
|
||||
Name: "myProject",
|
||||
WorkingDir: "testdata",
|
||||
Networks: types.Networks{},
|
||||
ComposeFiles: []string{filepath.Join("testdata", "simple", "compose.yaml"), filepath.Join("testdata", "simple", "compose-with-overrides.yaml")},
|
||||
}
|
||||
absWorkingDir, _ := filepath.Abs("testdata")
|
||||
absComposeFile, _ := filepath.Abs(filepath.Join("testdata", "simple", "compose.yaml"))
|
||||
absOverrideFile, _ := filepath.Abs(filepath.Join("testdata", "simple", "compose-with-overrides.yaml"))
|
||||
|
||||
expected := types.Project{
|
||||
Name: "myProject",
|
||||
Networks: types.Networks{"default": {Name: "myProject_default"}},
|
||||
WorkingDir: absWorkingDir,
|
||||
ComposeFiles: []string{absComposeFile, absOverrideFile},
|
||||
}
|
||||
err := normalize(&project, false)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, expected, project)
|
||||
}
|
||||
|
||||
func TestNormalizeVolumes(t *testing.T) {
|
||||
project := types.Project{
|
||||
Name: "myProject",
|
||||
Networks: types.Networks{},
|
||||
Volumes: types.Volumes{
|
||||
"myExternalVol": {
|
||||
Name: "myExternalVol", // this is automaticaly setup by loader for externa networks before reaching normalization
|
||||
External: types.External{External: true},
|
||||
},
|
||||
"myvol": {},
|
||||
"myNamedVol": {
|
||||
Name: "CustomName",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
absCwd, _ := filepath.Abs(".")
|
||||
expected := types.Project{
|
||||
Name: "myProject",
|
||||
Networks: types.Networks{"default": {Name: "myProject_default"}},
|
||||
Volumes: types.Volumes{
|
||||
"myExternalVol": {
|
||||
Name: "myExternalVol",
|
||||
External: types.External{External: true},
|
||||
},
|
||||
"myvol": {Name: "myProject_myvol"},
|
||||
"myNamedVol": {
|
||||
Name: "CustomName",
|
||||
},
|
||||
},
|
||||
WorkingDir: absCwd,
|
||||
ComposeFiles: []string{},
|
||||
}
|
||||
err := normalize(&project, false)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, expected, project)
|
||||
}
|
17
pkg/third_party/compose-go/loader/testdata/Dockerfile
vendored
Executable file
17
pkg/third_party/compose-go/loader/testdata/Dockerfile
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
# Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM busybox:1.31.0-uclibc
|
||||
RUN echo something
|
||||
CMD top
|
3
pkg/third_party/compose-go/loader/testdata/compose-test-extends-imported.yaml
vendored
Executable file
3
pkg/third_party/compose-go/loader/testdata/compose-test-extends-imported.yaml
vendored
Executable file
@ -0,0 +1,3 @@
|
||||
services:
|
||||
imported:
|
||||
image: nginx
|
5
pkg/third_party/compose-go/loader/testdata/compose-test-extends.yaml
vendored
Executable file
5
pkg/third_party/compose-go/loader/testdata/compose-test-extends.yaml
vendored
Executable file
@ -0,0 +1,5 @@
|
||||
services:
|
||||
importer:
|
||||
extends:
|
||||
file: compose-test-extends-imported.yaml
|
||||
service: imported
|
23
pkg/third_party/compose-go/loader/testdata/compose-test-with-version.yaml
vendored
Executable file
23
pkg/third_party/compose-go/loader/testdata/compose-test-with-version.yaml
vendored
Executable file
@ -0,0 +1,23 @@
|
||||
version: "2"
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
front: {}
|
||||
|
||||
services:
|
||||
web:
|
||||
build: ./Dockerfile
|
||||
networks:
|
||||
- front
|
||||
- default
|
||||
volumes_from:
|
||||
- other
|
||||
|
||||
other:
|
||||
image: busybox:1.31.0-uclibc
|
||||
command: top
|
||||
volumes:
|
||||
- /data
|
67
pkg/third_party/compose-go/loader/types_test.go
vendored
Executable file
67
pkg/third_party/compose-go/loader/types_test.go
vendored
Executable file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestMarshallConfig(t *testing.T) {
|
||||
workingDir, err := os.Getwd()
|
||||
assert.NilError(t, err)
|
||||
homeDir, err := os.UserHomeDir()
|
||||
assert.NilError(t, err)
|
||||
cfg := fullExampleConfig(workingDir, homeDir)
|
||||
expected := fullExampleYAML(workingDir, homeDir)
|
||||
|
||||
actual, err := yaml.Marshal(cfg)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(expected, string(actual)))
|
||||
|
||||
// Make sure the expected still
|
||||
_, err = Load(buildConfigDetails(expected, map[string]string{}), func(options *Options) {
|
||||
options.SkipNormalization = true
|
||||
options.SkipConsistencyCheck = true
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
func TestJSONMarshallConfig(t *testing.T) {
|
||||
workingDir, err := os.Getwd()
|
||||
assert.NilError(t, err)
|
||||
homeDir, err := os.UserHomeDir()
|
||||
assert.NilError(t, err)
|
||||
|
||||
cfg := fullExampleConfig(workingDir, homeDir)
|
||||
expected := fullExampleJSON(workingDir, homeDir)
|
||||
|
||||
actual, err := json.MarshalIndent(cfg, "", " ")
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(expected, string(actual)))
|
||||
|
||||
_, err = Load(buildConfigDetails(expected, map[string]string{}), func(options *Options) {
|
||||
options.SkipNormalization = true
|
||||
options.SkipConsistencyCheck = true
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
}
|
70
pkg/third_party/compose-go/loader/validate.go
vendored
Executable file
70
pkg/third_party/compose-go/loader/validate.go
vendored
Executable file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/errdefs"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// checkConsistency validate a compose model is consistent
|
||||
func checkConsistency(project *types.Project) error {
|
||||
for _, s := range project.Services {
|
||||
if s.Build == nil && s.Image == "" {
|
||||
return errors.Wrapf(errdefs.ErrInvalid, "service %q has neither an image nor a build context specified", s.Name)
|
||||
}
|
||||
|
||||
for network := range s.Networks {
|
||||
if _, ok := project.Networks[network]; !ok {
|
||||
return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined network %s", s.Name, network))
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s.NetworkMode, types.ServicePrefix) {
|
||||
serviceName := s.NetworkMode[len(types.ServicePrefix):]
|
||||
if _, err := project.GetServices(serviceName); err != nil {
|
||||
return fmt.Errorf("service %q not found for network_mode 'service:%s'", serviceName, serviceName)
|
||||
}
|
||||
}
|
||||
|
||||
for _, volume := range s.Volumes {
|
||||
switch volume.Type {
|
||||
case types.VolumeTypeVolume:
|
||||
if volume.Source != "" { // non anonymous volumes
|
||||
if _, ok := project.Volumes[volume.Source]; !ok {
|
||||
return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined volume %s", s.Name, volume.Source))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, secret := range s.Secrets {
|
||||
if _, ok := project.Secrets[secret.Source]; !ok {
|
||||
return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined secret %s", s.Name, secret.Source))
|
||||
}
|
||||
}
|
||||
for _, config := range s.Configs {
|
||||
if _, ok := project.Configs[config.Source]; !ok {
|
||||
return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined config %s", s.Name, config.Source))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
141
pkg/third_party/compose-go/loader/validate_test.go
vendored
Executable file
141
pkg/third_party/compose-go/loader/validate_test.go
vendored
Executable file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
func TestValidateAnonymousVolume(t *testing.T) {
|
||||
project := &types.Project{
|
||||
Services: types.Services([]types.ServiceConfig{
|
||||
{
|
||||
Name: "myservice",
|
||||
Image: "my/service",
|
||||
Volumes: []types.ServiceVolumeConfig{
|
||||
{
|
||||
Type: types.VolumeTypeVolume,
|
||||
Target: "/use/local",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
err := checkConsistency(project)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateNamedVolume(t *testing.T) {
|
||||
project := &types.Project{
|
||||
Services: types.Services([]types.ServiceConfig{
|
||||
{
|
||||
Name: "myservice",
|
||||
Image: "my/service",
|
||||
Volumes: []types.ServiceVolumeConfig{
|
||||
{
|
||||
Type: types.VolumeTypeVolume,
|
||||
Source: "myVolume",
|
||||
Target: "/use/local",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
err := checkConsistency(project)
|
||||
assert.Error(t, err, `service "myservice" refers to undefined volume myVolume: invalid compose project`)
|
||||
|
||||
project.Volumes = types.Volumes(map[string]types.VolumeConfig{
|
||||
"myVolume": {
|
||||
Name: "myVolume",
|
||||
},
|
||||
})
|
||||
err = checkConsistency(project)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateNoBuildNoImage(t *testing.T) {
|
||||
project := &types.Project{
|
||||
Services: types.Services([]types.ServiceConfig{
|
||||
{
|
||||
Name: "myservice",
|
||||
},
|
||||
}),
|
||||
}
|
||||
err := checkConsistency(project)
|
||||
assert.Error(t, err, `service "myservice" has neither an image nor a build context specified: invalid compose project`)
|
||||
}
|
||||
|
||||
func TestValidateNetworkMode(t *testing.T) {
|
||||
t.Run("network_mode service fail", func(t *testing.T) {
|
||||
project := &types.Project{
|
||||
Services: types.Services([]types.ServiceConfig{
|
||||
{
|
||||
Name: "myservice1",
|
||||
Image: "scratch",
|
||||
},
|
||||
{
|
||||
Name: "myservice2",
|
||||
Image: "scratch",
|
||||
NetworkMode: "service:myservice1",
|
||||
},
|
||||
}),
|
||||
}
|
||||
err := checkConsistency(project)
|
||||
assert.NilError(t, err)
|
||||
})
|
||||
|
||||
t.Run("network_mode service fail", func(t *testing.T) {
|
||||
project := &types.Project{
|
||||
Services: types.Services([]types.ServiceConfig{
|
||||
{
|
||||
Name: "myservice1",
|
||||
Image: "scratch",
|
||||
},
|
||||
{
|
||||
Name: "myservice2",
|
||||
Image: "scratch",
|
||||
NetworkMode: "service:nonexistentservice",
|
||||
},
|
||||
}),
|
||||
}
|
||||
err := checkConsistency(project)
|
||||
assert.Error(t, err, `service "nonexistentservice" not found for network_mode 'service:nonexistentservice'`)
|
||||
})
|
||||
|
||||
t.Run("network_mode container", func(t *testing.T) {
|
||||
project := &types.Project{
|
||||
Services: types.Services([]types.ServiceConfig{
|
||||
{
|
||||
Name: "myservice1",
|
||||
ContainerName: "mycontainer_name",
|
||||
Image: "scratch",
|
||||
},
|
||||
{
|
||||
Name: "myservice2",
|
||||
Image: "scratch",
|
||||
NetworkMode: "container:mycontainer_name",
|
||||
},
|
||||
}),
|
||||
}
|
||||
err := checkConsistency(project)
|
||||
assert.NilError(t, err)
|
||||
})
|
||||
}
|
154
pkg/third_party/compose-go/loader/volume.go
vendored
Executable file
154
pkg/third_party/compose-go/loader/volume.go
vendored
Executable file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const endOfSpec = rune(0)
|
||||
|
||||
// ParseVolume parses a volume spec without any knowledge of the target platform
|
||||
func ParseVolume(spec string) (types.ServiceVolumeConfig, error) {
|
||||
volume := types.ServiceVolumeConfig{}
|
||||
|
||||
switch len(spec) {
|
||||
case 0:
|
||||
return volume, errors.New("invalid empty volume spec")
|
||||
case 1, 2:
|
||||
volume.Target = spec
|
||||
volume.Type = string(types.VolumeTypeVolume)
|
||||
return volume, nil
|
||||
}
|
||||
|
||||
buffer := []rune{}
|
||||
for _, char := range spec + string(endOfSpec) {
|
||||
switch {
|
||||
case isWindowsDrive(buffer, char):
|
||||
buffer = append(buffer, char)
|
||||
case char == ':' || char == endOfSpec:
|
||||
if err := populateFieldFromBuffer(char, buffer, &volume); err != nil {
|
||||
populateType(&volume)
|
||||
return volume, errors.Wrapf(err, "invalid spec: %s", spec)
|
||||
}
|
||||
buffer = []rune{}
|
||||
default:
|
||||
buffer = append(buffer, char)
|
||||
}
|
||||
}
|
||||
|
||||
populateType(&volume)
|
||||
return volume, nil
|
||||
}
|
||||
|
||||
func isWindowsDrive(buffer []rune, char rune) bool {
|
||||
return char == ':' && len(buffer) == 1 && unicode.IsLetter(buffer[0])
|
||||
}
|
||||
|
||||
func populateFieldFromBuffer(char rune, buffer []rune, volume *types.ServiceVolumeConfig) error {
|
||||
strBuffer := string(buffer)
|
||||
switch {
|
||||
case len(buffer) == 0:
|
||||
return errors.New("empty section between colons")
|
||||
// Anonymous volume
|
||||
case volume.Source == "" && char == endOfSpec:
|
||||
volume.Target = strBuffer
|
||||
return nil
|
||||
case volume.Source == "":
|
||||
volume.Source = strBuffer
|
||||
return nil
|
||||
case volume.Target == "":
|
||||
volume.Target = strBuffer
|
||||
return nil
|
||||
case char == ':':
|
||||
return errors.New("too many colons")
|
||||
}
|
||||
for _, option := range strings.Split(strBuffer, ",") {
|
||||
switch option {
|
||||
case "ro":
|
||||
volume.ReadOnly = true
|
||||
case "rw":
|
||||
volume.ReadOnly = false
|
||||
case "nocopy":
|
||||
volume.Volume = &types.ServiceVolumeVolume{NoCopy: true}
|
||||
default:
|
||||
if isBindOption(option) {
|
||||
volume.Bind = &types.ServiceVolumeBind{Propagation: option}
|
||||
}
|
||||
// ignore unknown options
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var Propagations = []string{
|
||||
types.PropagationRPrivate,
|
||||
types.PropagationPrivate,
|
||||
types.PropagationRShared,
|
||||
types.PropagationShared,
|
||||
types.PropagationRSlave,
|
||||
types.PropagationSlave,
|
||||
}
|
||||
|
||||
func isBindOption(option string) bool {
|
||||
for _, propagation := range Propagations {
|
||||
if option == propagation {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func populateType(volume *types.ServiceVolumeConfig) {
|
||||
if isFilePath(volume.Source) {
|
||||
volume.Type = types.VolumeTypeBind
|
||||
if volume.Bind == nil {
|
||||
volume.Bind = &types.ServiceVolumeBind{}
|
||||
}
|
||||
// For backward compatibility with docker-compose legacy, using short notation involves
|
||||
// bind will create missing host path
|
||||
volume.Bind.CreateHostPath = true
|
||||
} else {
|
||||
volume.Type = types.VolumeTypeVolume
|
||||
if volume.Volume == nil {
|
||||
volume.Volume = &types.ServiceVolumeVolume{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isFilePath(source string) bool {
|
||||
if source == "" {
|
||||
return false
|
||||
}
|
||||
switch source[0] {
|
||||
case '.', '/', '~':
|
||||
return true
|
||||
}
|
||||
|
||||
// windows named pipes
|
||||
if strings.HasPrefix(source, `\\`) {
|
||||
return true
|
||||
}
|
||||
|
||||
first, nextIndex := utf8.DecodeRuneInString(source)
|
||||
return isWindowsDrive([]rune{first}, rune(source[nextIndex]))
|
||||
}
|
250
pkg/third_party/compose-go/loader/volume_test.go
vendored
Executable file
250
pkg/third_party/compose-go/loader/volume_test.go
vendored
Executable file
@ -0,0 +1,250 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestParseVolumeAnonymousVolume(t *testing.T) {
|
||||
for _, path := range []string{"/path", "/path/foo"} {
|
||||
volume, err := ParseVolume(path)
|
||||
expected := types.ServiceVolumeConfig{Type: "volume", Target: path, Volume: &types.ServiceVolumeVolume{}}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolumeAnonymousVolumeWindows(t *testing.T) {
|
||||
for _, path := range []string{"C:\\path", "Z:\\path\\foo"} {
|
||||
volume, err := ParseVolume(path)
|
||||
expected := types.ServiceVolumeConfig{Type: "volume", Target: path, Volume: &types.ServiceVolumeVolume{}}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolumeTooManyColons(t *testing.T) {
|
||||
_, err := ParseVolume("/foo:/foo:ro:foo")
|
||||
assert.Error(t, err, "invalid spec: /foo:/foo:ro:foo: too many colons")
|
||||
}
|
||||
|
||||
func TestParseVolumeShortVolumes(t *testing.T) {
|
||||
for _, path := range []string{".", "/a"} {
|
||||
volume, err := ParseVolume(path)
|
||||
expected := types.ServiceVolumeConfig{Type: "volume", Target: path}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolumeMissingSource(t *testing.T) {
|
||||
for _, spec := range []string{":foo", "/foo::ro"} {
|
||||
_, err := ParseVolume(spec)
|
||||
assert.ErrorContains(t, err, "empty section between colons")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolumeBindMount(t *testing.T) {
|
||||
for _, path := range []string{"./foo", "~/thing", "../other", "/foo", "/home/user"} {
|
||||
volume, err := ParseVolume(path + ":/target")
|
||||
expected := types.ServiceVolumeConfig{
|
||||
Type: "bind",
|
||||
Source: path,
|
||||
Target: "/target",
|
||||
Bind: &types.ServiceVolumeBind{CreateHostPath: true},
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolumeRelativeBindMountWindows(t *testing.T) {
|
||||
for _, path := range []string{
|
||||
"./foo",
|
||||
"~/thing",
|
||||
"../other",
|
||||
"D:\\path", "/home/user",
|
||||
} {
|
||||
volume, err := ParseVolume(path + ":d:\\target")
|
||||
expected := types.ServiceVolumeConfig{
|
||||
Type: "bind",
|
||||
Source: path,
|
||||
Target: "d:\\target",
|
||||
Bind: &types.ServiceVolumeBind{CreateHostPath: true},
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolumeWithBindOptions(t *testing.T) {
|
||||
volume, err := ParseVolume("/source:/target:slave")
|
||||
expected := types.ServiceVolumeConfig{
|
||||
Type: "bind",
|
||||
Source: "/source",
|
||||
Target: "/target",
|
||||
Bind: &types.ServiceVolumeBind{
|
||||
CreateHostPath: true,
|
||||
Propagation: "slave",
|
||||
},
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
|
||||
func TestParseVolumeWithBindOptionsWindows(t *testing.T) {
|
||||
volume, err := ParseVolume("C:\\source\\foo:D:\\target:ro,rprivate")
|
||||
expected := types.ServiceVolumeConfig{
|
||||
Type: "bind",
|
||||
Source: "C:\\source\\foo",
|
||||
Target: "D:\\target",
|
||||
ReadOnly: true,
|
||||
Bind: &types.ServiceVolumeBind{
|
||||
CreateHostPath: true,
|
||||
Propagation: "rprivate",
|
||||
},
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
|
||||
func TestParseVolumeWithInvalidVolumeOptions(t *testing.T) {
|
||||
_, err := ParseVolume("name:/target:bogus")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
func TestParseVolumeWithVolumeOptions(t *testing.T) {
|
||||
volume, err := ParseVolume("name:/target:nocopy")
|
||||
expected := types.ServiceVolumeConfig{
|
||||
Type: "volume",
|
||||
Source: "name",
|
||||
Target: "/target",
|
||||
Volume: &types.ServiceVolumeVolume{NoCopy: true},
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
|
||||
func TestParseVolumeWithReadOnly(t *testing.T) {
|
||||
for _, path := range []string{"./foo", "/home/user"} {
|
||||
volume, err := ParseVolume(path + ":/target:ro")
|
||||
expected := types.ServiceVolumeConfig{
|
||||
Type: "bind",
|
||||
Source: path,
|
||||
Target: "/target",
|
||||
ReadOnly: true,
|
||||
Bind: &types.ServiceVolumeBind{CreateHostPath: true},
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolumeWithRW(t *testing.T) {
|
||||
for _, path := range []string{"./foo", "/home/user"} {
|
||||
volume, err := ParseVolume(path + ":/target:rw")
|
||||
expected := types.ServiceVolumeConfig{
|
||||
Type: "bind",
|
||||
Source: path,
|
||||
Target: "/target",
|
||||
ReadOnly: false,
|
||||
Bind: &types.ServiceVolumeBind{CreateHostPath: true},
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolumeWindowsNamedPipe(t *testing.T) {
|
||||
volume, err := ParseVolume(`\\.\pipe\docker_engine:\\.\pipe\inside`)
|
||||
assert.NilError(t, err)
|
||||
expected := types.ServiceVolumeConfig{
|
||||
Type: "bind",
|
||||
Source: `\\.\pipe\docker_engine`,
|
||||
Target: `\\.\pipe\inside`,
|
||||
Bind: &types.ServiceVolumeBind{CreateHostPath: true},
|
||||
}
|
||||
assert.Check(t, is.DeepEqual(expected, volume))
|
||||
}
|
||||
|
||||
func TestIsFilePath(t *testing.T) {
|
||||
assert.Check(t, !isFilePath("a界"))
|
||||
}
|
||||
|
||||
// Preserve the test cases for VolumeSplitN
|
||||
func TestParseVolumeSplitCases(t *testing.T) {
|
||||
for casenumber, x := range []struct {
|
||||
input string
|
||||
n int
|
||||
expected []string
|
||||
}{
|
||||
{`C:\foo:d:`, -1, []string{`C:\foo`, `d:`}},
|
||||
{`:C:\foo:d:`, -1, nil},
|
||||
{`/foo:/bar:ro`, 3, []string{`/foo`, `/bar`, `ro`}},
|
||||
{`/foo:/bar:ro`, 2, []string{`/foo`, `/bar:ro`}},
|
||||
{`C:\foo\:/foo`, -1, []string{`C:\foo\`, `/foo`}},
|
||||
{`d:\`, -1, []string{`d:\`}},
|
||||
{`d:`, -1, []string{`d:`}},
|
||||
{`d:\path`, -1, []string{`d:\path`}},
|
||||
{`d:\path with space`, -1, []string{`d:\path with space`}},
|
||||
{`d:\pathandmode:rw`, -1, []string{`d:\pathandmode`, `rw`}},
|
||||
|
||||
{`c:\:d:\`, -1, []string{`c:\`, `d:\`}},
|
||||
{`c:\windows\:d:`, -1, []string{`c:\windows\`, `d:`}},
|
||||
{`c:\windows:d:\s p a c e`, -1, []string{`c:\windows`, `d:\s p a c e`}},
|
||||
{`c:\windows:d:\s p a c e:RW`, -1, []string{`c:\windows`, `d:\s p a c e`, `RW`}},
|
||||
{`c:\program files:d:\s p a c e i n h o s t d i r`, -1, []string{`c:\program files`, `d:\s p a c e i n h o s t d i r`}},
|
||||
{`0123456789name:d:`, -1, []string{`0123456789name`, `d:`}},
|
||||
{`MiXeDcAsEnAmE:d:`, -1, []string{`MiXeDcAsEnAmE`, `d:`}},
|
||||
{`name:D:`, -1, []string{`name`, `D:`}},
|
||||
{`name:D::rW`, -1, []string{`name`, `D:`, `rW`}},
|
||||
{`name:D::RW`, -1, []string{`name`, `D:`, `RW`}},
|
||||
|
||||
{`c:/:d:/forward/slashes/are/good/too`, -1, []string{`c:/`, `d:/forward/slashes/are/good/too`}},
|
||||
{`c:\Windows`, -1, []string{`c:\Windows`}},
|
||||
{`c:\Program Files (x86)`, -1, []string{`c:\Program Files (x86)`}},
|
||||
{``, -1, nil},
|
||||
{`.`, -1, []string{`.`}},
|
||||
{`..\`, -1, []string{`..\`}},
|
||||
{`c:\:..\`, -1, []string{`c:\`, `..\`}},
|
||||
{`c:\:d:\:xyzzy`, -1, []string{`c:\`, `d:\`, `xyzzy`}},
|
||||
// Cover directories with one-character name
|
||||
{`/tmp/x/y:/foo/x/y`, -1, []string{`/tmp/x/y`, `/foo/x/y`}},
|
||||
} {
|
||||
parsed, _ := ParseVolume(x.input)
|
||||
|
||||
expected := len(x.expected) > 1
|
||||
msg := fmt.Sprintf("Case %d: %s", casenumber, x.input)
|
||||
assert.Check(t, is.Equal(expected, parsed.Source != ""), msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolumeInvalidEmptySpec(t *testing.T) {
|
||||
_, err := ParseVolume("")
|
||||
assert.ErrorContains(t, err, "invalid empty volume spec")
|
||||
}
|
||||
|
||||
func TestParseVolumeInvalidSections(t *testing.T) {
|
||||
_, err := ParseVolume("/foo::rw")
|
||||
assert.ErrorContains(t, err, "invalid spec")
|
||||
}
|
82
pkg/third_party/compose-go/loader/windows_path.go
vendored
Executable file
82
pkg/third_party/compose-go/loader/windows_path.go
vendored
Executable file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// https://github.com/golang/go/blob/master/LICENSE
|
||||
|
||||
// This file contains utilities to check for Windows absolute paths on Linux.
|
||||
// The code in this file was largely copied from the Golang filepath package
|
||||
// https://github.com/golang/go/blob/1d0e94b1e13d5e8a323a63cd1cc1ef95290c9c36/src/path/filepath/path_windows.go#L12-L65
|
||||
|
||||
func isSlash(c uint8) bool {
|
||||
return c == '\\' || c == '/'
|
||||
}
|
||||
|
||||
// isAbs reports whether the path is a Windows absolute path.
|
||||
func isAbs(path string) (b bool) {
|
||||
l := volumeNameLen(path)
|
||||
if l == 0 {
|
||||
return false
|
||||
}
|
||||
path = path[l:]
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
return isSlash(path[0])
|
||||
}
|
||||
|
||||
// volumeNameLen returns length of the leading volume name on Windows.
|
||||
// It returns 0 elsewhere.
|
||||
// nolint: gocyclo
|
||||
func volumeNameLen(path string) int {
|
||||
if len(path) < 2 {
|
||||
return 0
|
||||
}
|
||||
// with drive letter
|
||||
c := path[0]
|
||||
if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
|
||||
return 2
|
||||
}
|
||||
// is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
||||
if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
|
||||
!isSlash(path[2]) && path[2] != '.' {
|
||||
// first, leading `\\` and next shouldn't be `\`. its server name.
|
||||
for n := 3; n < l-1; n++ {
|
||||
// second, next '\' shouldn't be repeated.
|
||||
if isSlash(path[n]) {
|
||||
n++
|
||||
// third, following something characters. its share name.
|
||||
if !isSlash(path[n]) {
|
||||
if path[n] == '.' {
|
||||
break
|
||||
}
|
||||
for ; n < l; n++ {
|
||||
if isSlash(path[n]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
78
pkg/third_party/compose-go/loader/windows_path_test.go
vendored
Executable file
78
pkg/third_party/compose-go/loader/windows_path_test.go
vendored
Executable file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// https://github.com/golang/go/blob/master/LICENSE
|
||||
|
||||
// The code in this file was copied from the Golang filepath package with some
|
||||
// small modifications to run it on non-Windows platforms.
|
||||
// https://github.com/golang/go/blob/1d0e94b1e13d5e8a323a63cd1cc1ef95290c9c36/src/path/filepath/path_test.go#L711-L763
|
||||
|
||||
import "testing"
|
||||
|
||||
type IsAbsTest struct {
|
||||
path string
|
||||
isAbs bool
|
||||
}
|
||||
|
||||
var isabstests = []IsAbsTest{
|
||||
{"", false},
|
||||
{"/", true},
|
||||
{"/usr/bin/gcc", true},
|
||||
{"..", false},
|
||||
{"/a/../bb", true},
|
||||
{".", false},
|
||||
{"./", false},
|
||||
{"lala", false},
|
||||
}
|
||||
|
||||
var winisabstests = []IsAbsTest{
|
||||
{`C:\`, true},
|
||||
{`c\`, false},
|
||||
{`c::`, false},
|
||||
{`c:`, false},
|
||||
{`/`, false},
|
||||
{`\`, false},
|
||||
{`\Windows`, false},
|
||||
{`c:a\b`, false},
|
||||
{`c:\a\b`, true},
|
||||
{`c:/a/b`, true},
|
||||
{`\\host\share\foo`, true},
|
||||
{`//host/share/foo/bar`, true},
|
||||
}
|
||||
|
||||
func TestIsAbs(t *testing.T) {
|
||||
tests := winisabstests
|
||||
|
||||
// All non-windows tests should fail, because they have no volume letter.
|
||||
for _, test := range isabstests {
|
||||
tests = append(tests, IsAbsTest{test.path, false})
|
||||
}
|
||||
// All non-windows test should work as intended if prefixed with volume letter.
|
||||
for _, test := range isabstests {
|
||||
tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs})
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if r := isAbs(test.path); r != test.isAbs {
|
||||
t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
|
||||
}
|
||||
}
|
||||
}
|
73
pkg/third_party/compose-go/loader/with-version-struct_test.go
vendored
Executable file
73
pkg/third_party/compose-go/loader/with-version-struct_test.go
vendored
Executable file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
func withVersionExampleConfig(workingDir, homeDir string) *types.Config {
|
||||
return &types.Config{
|
||||
Services: withVersionServices(workingDir, homeDir),
|
||||
Networks: withVersionNetworks(),
|
||||
Volumes: withVersionVolumes(),
|
||||
}
|
||||
}
|
||||
|
||||
func withVersionServices(workingDir, homeDir string) []types.ServiceConfig {
|
||||
return []types.ServiceConfig{
|
||||
{
|
||||
Name: "web",
|
||||
|
||||
Build: &types.BuildConfig{
|
||||
Context: "./Dockerfile",
|
||||
},
|
||||
Environment: types.MappingWithEquals{},
|
||||
Networks: map[string]*types.ServiceNetworkConfig{
|
||||
"front": nil,
|
||||
"default": nil,
|
||||
},
|
||||
VolumesFrom: []string{"other"},
|
||||
Scale: 1,
|
||||
},
|
||||
{
|
||||
Name: "other",
|
||||
|
||||
Image: "busybox:1.31.0-uclibc",
|
||||
Command: []string{"top"},
|
||||
Environment: types.MappingWithEquals{},
|
||||
Volumes: []types.ServiceVolumeConfig{
|
||||
{Target: "/data", Type: "volume", Volume: &types.ServiceVolumeVolume{}},
|
||||
},
|
||||
Scale: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func withVersionNetworks() map[string]types.NetworkConfig {
|
||||
return map[string]types.NetworkConfig{
|
||||
"front": {},
|
||||
}
|
||||
}
|
||||
|
||||
func withVersionVolumes() map[string]types.VolumeConfig {
|
||||
return map[string]types.VolumeConfig{
|
||||
"data": {
|
||||
Driver: "local",
|
||||
},
|
||||
}
|
||||
}
|
813
pkg/third_party/compose-go/schema/compose-spec.json
vendored
Executable file
813
pkg/third_party/compose-go/schema/compose-spec.json
vendored
Executable file
@ -0,0 +1,813 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft/2019-09/schema#",
|
||||
"id": "compose_spec.json",
|
||||
"type": "object",
|
||||
"title": "Compose Specification",
|
||||
"description": "The Compose file is a YAML file defining a multi-containers based application.",
|
||||
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Version of the Compose specification used. Tools not implementing required version MUST reject the configuration file."
|
||||
},
|
||||
|
||||
"services": {
|
||||
"id": "#/properties/services",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"$ref": "#/definitions/service"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"networks": {
|
||||
"id": "#/properties/networks",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"$ref": "#/definitions/network"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"volumes": {
|
||||
"id": "#/properties/volumes",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"$ref": "#/definitions/volume"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"secrets": {
|
||||
"id": "#/properties/secrets",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"$ref": "#/definitions/secret"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"configs": {
|
||||
"id": "#/properties/configs",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"$ref": "#/definitions/config"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
|
||||
"patternProperties": {"^x-": {}},
|
||||
"additionalProperties": false,
|
||||
|
||||
"definitions": {
|
||||
|
||||
"service": {
|
||||
"id": "#/definitions/service",
|
||||
"type": "object",
|
||||
|
||||
"properties": {
|
||||
"deploy": {"$ref": "#/definitions/deployment"},
|
||||
"build": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"context": {"type": "string"},
|
||||
"dockerfile": {"type": "string"},
|
||||
"args": {"$ref": "#/definitions/list_or_dict"},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"cache_from": {"type": "array", "items": {"type": "string"}},
|
||||
"network": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"shm_size": {"type": ["integer", "string"]},
|
||||
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
||||
"isolation": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
]
|
||||
},
|
||||
"blkio_config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"device_read_bps": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/blkio_limit"}
|
||||
},
|
||||
"device_read_iops": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/blkio_limit"}
|
||||
},
|
||||
"device_write_bps": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/blkio_limit"}
|
||||
},
|
||||
"device_write_iops": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/blkio_limit"}
|
||||
},
|
||||
"weight": {"type": "integer"},
|
||||
"weight_device": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/blkio_weight"}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"cgroup_parent": {"type": "string"},
|
||||
"command": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "array", "items": {"type": "string"}}
|
||||
]
|
||||
},
|
||||
"configs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"uid": {"type": "string"},
|
||||
"gid": {"type": "string"},
|
||||
"mode": {"type": "number"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"container_name": {"type": "string"},
|
||||
"cpu_count": {"type": "integer", "minimum": 0},
|
||||
"cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100},
|
||||
"cpu_shares": {"type": ["number", "string"]},
|
||||
"cpu_quota": {"type": ["number", "string"]},
|
||||
"cpu_period": {"type": ["number", "string"]},
|
||||
"cpu_rt_period": {"type": ["number", "string"]},
|
||||
"cpu_rt_runtime": {"type": ["number", "string"]},
|
||||
"cpus": {"type": ["number", "string"]},
|
||||
"cpuset": {"type": "string"},
|
||||
"credential_spec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {"type": "string"},
|
||||
"file": {"type": "string"},
|
||||
"registry": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"depends_on": {
|
||||
"oneOf": [
|
||||
{"$ref": "#/definitions/list_of_strings"},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"condition": {
|
||||
"type": "string",
|
||||
"enum": ["service_started", "service_healthy", "service_completed_successfully"]
|
||||
}
|
||||
},
|
||||
"required": ["condition"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"},
|
||||
"devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"dns": {"$ref": "#/definitions/string_or_list"},
|
||||
"dns_opt": {"type": "array","items": {"type": "string"}, "uniqueItems": true},
|
||||
"dns_search": {"$ref": "#/definitions/string_or_list"},
|
||||
"domainname": {"type": "string"},
|
||||
"entrypoint": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "array", "items": {"type": "string"}}
|
||||
]
|
||||
},
|
||||
"env_file": {"$ref": "#/definitions/string_or_list"},
|
||||
"environment": {"$ref": "#/definitions/list_or_dict"},
|
||||
|
||||
"expose": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["string", "number"],
|
||||
"format": "expose"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"extends": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"type": "object",
|
||||
|
||||
"properties": {
|
||||
"service": {"type": "string"},
|
||||
"file": {"type": "string"}
|
||||
},
|
||||
"required": ["service"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
||||
"group_add": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["string", "number"]
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"healthcheck": {"$ref": "#/definitions/healthcheck"},
|
||||
"hostname": {"type": "string"},
|
||||
"image": {"type": "string"},
|
||||
"init": {"type": "boolean"},
|
||||
"ipc": {"type": "string"},
|
||||
"isolation": {"type": "string"},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"logging": {
|
||||
"type": "object",
|
||||
|
||||
"properties": {
|
||||
"driver": {"type": "string"},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number", "null"]}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"mac_address": {"type": "string"},
|
||||
"mem_limit": {"type": ["number", "string"]},
|
||||
"mem_reservation": {"type": ["string", "integer"]},
|
||||
"mem_swappiness": {"type": "integer"},
|
||||
"memswap_limit": {"type": ["number", "string"]},
|
||||
"network_mode": {"type": "string"},
|
||||
"networks": {
|
||||
"oneOf": [
|
||||
{"$ref": "#/definitions/list_of_strings"},
|
||||
{
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"aliases": {"$ref": "#/definitions/list_of_strings"},
|
||||
"ipv4_address": {"type": "string"},
|
||||
"ipv6_address": {"type": "string"},
|
||||
"link_local_ips": {"$ref": "#/definitions/list_of_strings"},
|
||||
"priority": {"type": "number"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
{"type": "null"}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"oom_kill_disable": {"type": "boolean"},
|
||||
"oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000},
|
||||
"pid": {"type": ["string", "null"]},
|
||||
"pids_limit": {"type": ["number", "string"]},
|
||||
"platform": {"type": "string"},
|
||||
"ports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"type": "number", "format": "ports"},
|
||||
{"type": "string", "format": "ports"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mode": {"type": "string"},
|
||||
"host_ip": {"type": "string"},
|
||||
"target": {"type": "integer"},
|
||||
"published": {"type": "integer"},
|
||||
"protocol": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"privileged": {"type": "boolean"},
|
||||
"profiles": {"$ref": "#/definitions/list_of_strings"},
|
||||
"pull_policy": {"type": "string", "enum": [
|
||||
"always", "never", "if_not_present", "build"
|
||||
]},
|
||||
"read_only": {"type": "boolean"},
|
||||
"restart": {"type": "string"},
|
||||
"runtime": {
|
||||
"type": "string"
|
||||
},
|
||||
"scale": {
|
||||
"type": "integer"
|
||||
},
|
||||
"security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"shm_size": {"type": ["number", "string"]},
|
||||
"secrets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"uid": {"type": "string"},
|
||||
"gid": {"type": "string"},
|
||||
"mode": {"type": "number"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sysctls": {"$ref": "#/definitions/list_or_dict"},
|
||||
"stdin_open": {"type": "boolean"},
|
||||
"stop_grace_period": {"type": "string", "format": "duration"},
|
||||
"stop_signal": {"type": "string"},
|
||||
"tmpfs": {"$ref": "#/definitions/string_or_list"},
|
||||
"tty": {"type": "boolean"},
|
||||
"ulimits": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-z]+$": {
|
||||
"oneOf": [
|
||||
{"type": "integer"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hard": {"type": "integer"},
|
||||
"soft": {"type": "integer"}
|
||||
},
|
||||
"required": ["soft", "hard"],
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {"type": "string"},
|
||||
"userns_mode": {"type": "string"},
|
||||
"volumes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["type"],
|
||||
"properties": {
|
||||
"type": {"type": "string"},
|
||||
"source": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"read_only": {"type": "boolean"},
|
||||
"consistency": {"type": "string"},
|
||||
"bind": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"propagation": {"type": "string"},
|
||||
"create_host_path": {"type": "boolean"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"volume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nocopy": {"type": "boolean"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"tmpfs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"volumes_from": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"working_dir": {"type": "string"}
|
||||
},
|
||||
"patternProperties": {"^x-": {}},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"healthcheck": {
|
||||
"id": "#/definitions/healthcheck",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"disable": {"type": "boolean"},
|
||||
"interval": {"type": "string", "format": "duration"},
|
||||
"retries": {"type": "number"},
|
||||
"test": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "array", "items": {"type": "string"}}
|
||||
]
|
||||
},
|
||||
"timeout": {"type": "string", "format": "duration"},
|
||||
"start_period": {"type": "string", "format": "duration"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"deployment": {
|
||||
"id": "#/definitions/deployment",
|
||||
"type": ["object", "null"],
|
||||
"properties": {
|
||||
"mode": {"type": "string"},
|
||||
"endpoint_mode": {"type": "string"},
|
||||
"replicas": {"type": "integer"},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"rollback_config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parallelism": {"type": "integer"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"failure_action": {"type": "string"},
|
||||
"monitor": {"type": "string", "format": "duration"},
|
||||
"max_failure_ratio": {"type": "number"},
|
||||
"order": {"type": "string", "enum": [
|
||||
"start-first", "stop-first"
|
||||
]}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"update_config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parallelism": {"type": "integer"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"failure_action": {"type": "string"},
|
||||
"monitor": {"type": "string", "format": "duration"},
|
||||
"max_failure_ratio": {"type": "number"},
|
||||
"order": {"type": "string", "enum": [
|
||||
"start-first", "stop-first"
|
||||
]}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"resources": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limits": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cpus": {"type": ["number", "string"]},
|
||||
"memory": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"reservations": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cpus": {"type": ["number", "string"]},
|
||||
"memory": {"type": "string"},
|
||||
"generic_resources": {"$ref": "#/definitions/generic_resources"},
|
||||
"devices": {"$ref": "#/definitions/devices"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"restart_policy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"condition": {"type": "string"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"max_attempts": {"type": "integer"},
|
||||
"window": {"type": "string", "format": "duration"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"placement": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"constraints": {"type": "array", "items": {"type": "string"}},
|
||||
"preferences": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"spread": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
},
|
||||
"max_replicas_per_node": {"type": "integer"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
|
||||
"generic_resources": {
|
||||
"id": "#/definitions/generic_resources",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"discrete_resource_spec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {"type": "string"},
|
||||
"value": {"type": "number"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
},
|
||||
|
||||
"devices": {
|
||||
"id": "#/definitions/devices",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"capabilities": {"$ref": "#/definitions/list_of_strings"},
|
||||
"count": {"type": ["string", "integer"]},
|
||||
"device_ids": {"$ref": "#/definitions/list_of_strings"},
|
||||
"driver":{"type": "string"},
|
||||
"options":{"$ref": "#/definitions/list_or_dict"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
},
|
||||
|
||||
"network": {
|
||||
"id": "#/definitions/network",
|
||||
"type": ["object", "null"],
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"driver": {"type": "string"},
|
||||
"driver_opts": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number"]}
|
||||
}
|
||||
},
|
||||
"ipam": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"driver": {"type": "string"},
|
||||
"config": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"subnet": {"type": "string", "format": "subnet_ip_address"},
|
||||
"ip_range": {"type": "string"},
|
||||
"gateway": {"type": "string"},
|
||||
"aux_addresses": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^.+$": {"type": "string"}}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^.+$": {"type": "string"}}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"deprecated": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"internal": {"type": "boolean"},
|
||||
"enable_ipv6": {"type": "boolean"},
|
||||
"attachable": {"type": "boolean"},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
|
||||
"volume": {
|
||||
"id": "#/definitions/volume",
|
||||
"type": ["object", "null"],
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"driver": {"type": "string"},
|
||||
"driver_opts": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number"]}
|
||||
}
|
||||
},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"deprecated": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
|
||||
"secret": {
|
||||
"id": "#/definitions/secret",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"file": {"type": "string"},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"properties": {
|
||||
"name": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"driver": {"type": "string"},
|
||||
"driver_opts": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number"]}
|
||||
}
|
||||
},
|
||||
"template_driver": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
|
||||
"config": {
|
||||
"id": "#/definitions/config",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"file": {"type": "string"},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"deprecated": true,
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"template_driver": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
|
||||
"string_or_list": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"$ref": "#/definitions/list_of_strings"}
|
||||
]
|
||||
},
|
||||
|
||||
"list_of_strings": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"uniqueItems": true
|
||||
},
|
||||
|
||||
"list_or_dict": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".+": {
|
||||
"type": ["string", "number", "boolean", "null"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{"type": "array", "items": {"type": "string"}, "uniqueItems": true}
|
||||
]
|
||||
},
|
||||
|
||||
"blkio_limit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {"type": "string"},
|
||||
"rate": {"type": ["integer", "string"]}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"blkio_weight": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {"type": "string"},
|
||||
"weight": {"type": "integer"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"constraints": {
|
||||
"service": {
|
||||
"id": "#/definitions/constraints/service",
|
||||
"anyOf": [
|
||||
{"required": ["build"]},
|
||||
{"required": ["image"]}
|
||||
],
|
||||
"properties": {
|
||||
"build": {
|
||||
"required": ["context"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
169
pkg/third_party/compose-go/schema/schema.go
vendored
Executable file
169
pkg/third_party/compose-go/schema/schema.go
vendored
Executable file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
|
||||
// Enable support for embedded static resources
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultVersion = "1.0"
|
||||
versionField = "version"
|
||||
)
|
||||
|
||||
type portsFormatChecker struct{}
|
||||
|
||||
func (checker portsFormatChecker) IsFormat(input interface{}) bool {
|
||||
// TODO: implement this
|
||||
return true
|
||||
}
|
||||
|
||||
type durationFormatChecker struct{}
|
||||
|
||||
func (checker durationFormatChecker) IsFormat(input interface{}) bool {
|
||||
value, ok := input.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
_, err := time.ParseDuration(value)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
gojsonschema.FormatCheckers.Add("expose", portsFormatChecker{})
|
||||
gojsonschema.FormatCheckers.Add("ports", portsFormatChecker{})
|
||||
gojsonschema.FormatCheckers.Add("duration", durationFormatChecker{})
|
||||
}
|
||||
|
||||
// Schema is the compose-spec JSON schema
|
||||
//go:embed compose-spec.json
|
||||
var Schema string
|
||||
|
||||
// Validate uses the jsonschema to validate the configuration
|
||||
func Validate(config map[string]interface{}) error {
|
||||
schemaLoader := gojsonschema.NewStringLoader(Schema)
|
||||
dataLoader := gojsonschema.NewGoLoader(config)
|
||||
|
||||
result, err := gojsonschema.Validate(schemaLoader, dataLoader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !result.Valid() {
|
||||
return toError(result)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toError(result *gojsonschema.Result) error {
|
||||
err := getMostSpecificError(result.Errors())
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
jsonschemaOneOf = "number_one_of"
|
||||
jsonschemaAnyOf = "number_any_of"
|
||||
)
|
||||
|
||||
func getDescription(err validationError) string {
|
||||
switch err.parent.Type() {
|
||||
case "invalid_type":
|
||||
if expectedType, ok := err.parent.Details()["expected"].(string); ok {
|
||||
return fmt.Sprintf("must be a %s", humanReadableType(expectedType))
|
||||
}
|
||||
case jsonschemaOneOf, jsonschemaAnyOf:
|
||||
if err.child == nil {
|
||||
return err.parent.Description()
|
||||
}
|
||||
return err.child.Description()
|
||||
}
|
||||
return err.parent.Description()
|
||||
}
|
||||
|
||||
func humanReadableType(definition string) string {
|
||||
if definition[0:1] == "[" {
|
||||
allTypes := strings.Split(definition[1:len(definition)-1], ",")
|
||||
for i, t := range allTypes {
|
||||
allTypes[i] = humanReadableType(t)
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%s or %s",
|
||||
strings.Join(allTypes[0:len(allTypes)-1], ", "),
|
||||
allTypes[len(allTypes)-1],
|
||||
)
|
||||
}
|
||||
if definition == "object" {
|
||||
return "mapping"
|
||||
}
|
||||
if definition == "array" {
|
||||
return "list"
|
||||
}
|
||||
return definition
|
||||
}
|
||||
|
||||
type validationError struct {
|
||||
parent gojsonschema.ResultError
|
||||
child gojsonschema.ResultError
|
||||
}
|
||||
|
||||
func (err validationError) Error() string {
|
||||
description := getDescription(err)
|
||||
return fmt.Sprintf("%s %s", err.parent.Field(), description)
|
||||
}
|
||||
|
||||
func getMostSpecificError(errors []gojsonschema.ResultError) validationError {
|
||||
mostSpecificError := 0
|
||||
for i, err := range errors {
|
||||
if specificity(err) > specificity(errors[mostSpecificError]) {
|
||||
mostSpecificError = i
|
||||
continue
|
||||
}
|
||||
|
||||
if specificity(err) == specificity(errors[mostSpecificError]) {
|
||||
// Invalid type errors win in a tie-breaker for most specific field name
|
||||
if err.Type() == "invalid_type" && errors[mostSpecificError].Type() != "invalid_type" {
|
||||
mostSpecificError = i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mostSpecificError+1 == len(errors) {
|
||||
return validationError{parent: errors[mostSpecificError]}
|
||||
}
|
||||
|
||||
switch errors[mostSpecificError].Type() {
|
||||
case "number_one_of", "number_any_of":
|
||||
return validationError{
|
||||
parent: errors[mostSpecificError],
|
||||
child: errors[mostSpecificError+1],
|
||||
}
|
||||
default:
|
||||
return validationError{parent: errors[mostSpecificError]}
|
||||
}
|
||||
}
|
||||
|
||||
func specificity(err gojsonschema.ResultError) int {
|
||||
return len(strings.Split(err.Field(), "."))
|
||||
}
|
235
pkg/third_party/compose-go/schema/schema_test.go
vendored
Executable file
235
pkg/third_party/compose-go/schema/schema_test.go
vendored
Executable file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
type dict map[string]interface{}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
config := dict{
|
||||
"services": dict{
|
||||
"foo": dict{
|
||||
"image": "busybox",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.NilError(t, Validate(config))
|
||||
}
|
||||
|
||||
func TestValidateUndefinedTopLevelOption(t *testing.T) {
|
||||
config := dict{
|
||||
"helicopters": dict{
|
||||
"foo": dict{
|
||||
"image": "busybox",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := Validate(config)
|
||||
assert.ErrorContains(t, err, "Additional property helicopters is not allowed")
|
||||
}
|
||||
|
||||
func TestValidateAllowsXTopLevelFields(t *testing.T) {
|
||||
config := dict{
|
||||
"x-extra-stuff": dict{},
|
||||
}
|
||||
|
||||
assert.NilError(t, Validate(config))
|
||||
assert.NilError(t, Validate(config))
|
||||
}
|
||||
|
||||
func TestValidateAllowsXFields(t *testing.T) {
|
||||
config := dict{
|
||||
"services": dict{
|
||||
"bar": dict{
|
||||
"x-extra-stuff": dict{},
|
||||
},
|
||||
},
|
||||
"volumes": dict{
|
||||
"bar": dict{
|
||||
"x-extra-stuff": dict{},
|
||||
},
|
||||
},
|
||||
"networks": dict{
|
||||
"bar": dict{
|
||||
"x-extra-stuff": dict{},
|
||||
},
|
||||
},
|
||||
"configs": dict{
|
||||
"bar": dict{
|
||||
"x-extra-stuff": dict{},
|
||||
},
|
||||
},
|
||||
"secrets": dict{
|
||||
"bar": dict{
|
||||
"x-extra-stuff": dict{},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.NilError(t, Validate(config))
|
||||
assert.NilError(t, Validate(config))
|
||||
}
|
||||
|
||||
func TestValidateSecretConfigNames(t *testing.T) {
|
||||
config := dict{
|
||||
"configs": dict{
|
||||
"bar": dict{
|
||||
"name": "foobar",
|
||||
},
|
||||
},
|
||||
"secrets": dict{
|
||||
"baz": dict{
|
||||
"name": "foobaz",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.NilError(t, Validate(config))
|
||||
assert.NilError(t, Validate(config))
|
||||
}
|
||||
|
||||
type array []interface{}
|
||||
|
||||
func TestValidatePlacement(t *testing.T) {
|
||||
config := dict{
|
||||
"services": dict{
|
||||
"foo": dict{
|
||||
"image": "busybox",
|
||||
"deploy": dict{
|
||||
"placement": dict{
|
||||
"preferences": array{
|
||||
dict{
|
||||
"spread": "node.labels.az",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.NilError(t, Validate(config))
|
||||
assert.NilError(t, Validate(config))
|
||||
}
|
||||
|
||||
func TestValidateIsolation(t *testing.T) {
|
||||
config := dict{
|
||||
"services": dict{
|
||||
"foo": dict{
|
||||
"image": "busybox",
|
||||
"isolation": "some-isolation-value",
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.NilError(t, Validate(config))
|
||||
assert.NilError(t, Validate(config))
|
||||
}
|
||||
|
||||
func TestValidateRollbackConfig(t *testing.T) {
|
||||
config := dict{
|
||||
"services": dict{
|
||||
"foo": dict{
|
||||
"image": "busybox",
|
||||
"deploy": dict{
|
||||
"rollback_config": dict{
|
||||
"parallelism": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.NilError(t, Validate(config))
|
||||
assert.NilError(t, Validate(config))
|
||||
}
|
||||
|
||||
func TestValidateRollbackConfigWithOrder(t *testing.T) {
|
||||
config := dict{
|
||||
"services": dict{
|
||||
"foo": dict{
|
||||
"image": "busybox",
|
||||
"deploy": dict{
|
||||
"rollback_config": dict{
|
||||
"parallelism": 1,
|
||||
"order": "start-first",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.NilError(t, Validate(config))
|
||||
assert.NilError(t, Validate(config))
|
||||
}
|
||||
|
||||
func TestValidateRollbackConfigWithUpdateConfig(t *testing.T) {
|
||||
config := dict{
|
||||
"services": dict{
|
||||
"foo": dict{
|
||||
"image": "busybox",
|
||||
"deploy": dict{
|
||||
"update_config": dict{
|
||||
"parallelism": 1,
|
||||
"order": "start-first",
|
||||
},
|
||||
"rollback_config": dict{
|
||||
"parallelism": 1,
|
||||
"order": "start-first",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.NilError(t, Validate(config))
|
||||
assert.NilError(t, Validate(config))
|
||||
}
|
||||
|
||||
func TestValidateRollbackConfigWithUpdateConfigFull(t *testing.T) {
|
||||
config := dict{
|
||||
"services": dict{
|
||||
"foo": dict{
|
||||
"image": "busybox",
|
||||
"deploy": dict{
|
||||
"update_config": dict{
|
||||
"parallelism": 1,
|
||||
"order": "start-first",
|
||||
"delay": "10s",
|
||||
"failure_action": "pause",
|
||||
"monitor": "10s",
|
||||
},
|
||||
"rollback_config": dict{
|
||||
"parallelism": 1,
|
||||
"order": "start-first",
|
||||
"delay": "10s",
|
||||
"failure_action": "pause",
|
||||
"monitor": "10s",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.NilError(t, Validate(config))
|
||||
assert.NilError(t, Validate(config))
|
||||
}
|
28
pkg/third_party/compose-go/scripts/validate/fileheader
vendored
Executable file
28
pkg/third_party/compose-go/scripts/validate/fileheader
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright The Compose Specification Authors.
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
set -eu -o pipefail
|
||||
|
||||
if ! command -v ltag; then
|
||||
>&2 echo "ERROR: ltag not found. Install with:"
|
||||
>&2 echo " go get -u github.com/kunalkushwaha/ltag"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BASEPATH="${1-}"
|
||||
|
||||
ltag -t "${BASEPATH}scripts/validate/template" --excludes "validate" --check -v
|
14
pkg/third_party/compose-go/scripts/validate/template/bash.txt
vendored
Executable file
14
pkg/third_party/compose-go/scripts/validate/template/bash.txt
vendored
Executable file
@ -0,0 +1,14 @@
|
||||
# Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
14
pkg/third_party/compose-go/scripts/validate/template/dockerfile.txt
vendored
Executable file
14
pkg/third_party/compose-go/scripts/validate/template/dockerfile.txt
vendored
Executable file
@ -0,0 +1,14 @@
|
||||
# Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
11
vendor/github.com/containerd/continuity/sysx/nodata_unix.go → pkg/third_party/compose-go/scripts/validate/template/go.txt
vendored
Normal file → Executable file
11
vendor/github.com/containerd/continuity/sysx/nodata_unix.go → pkg/third_party/compose-go/scripts/validate/template/go.txt
vendored
Normal file → Executable file
@ -1,7 +1,5 @@
|
||||
// +build darwin freebsd openbsd
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -16,10 +14,3 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package sysx
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const ENODATA = syscall.ENOATTR
|
14
pkg/third_party/compose-go/scripts/validate/template/makefile.txt
vendored
Executable file
14
pkg/third_party/compose-go/scripts/validate/template/makefile.txt
vendored
Executable file
@ -0,0 +1,14 @@
|
||||
# Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
279
pkg/third_party/compose-go/template/template.go
vendored
Executable file
279
pkg/third_party/compose-go/template/template.go
vendored
Executable file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var delimiter = "\\$"
|
||||
var substitutionNamed = "[_a-z][_a-z0-9]*"
|
||||
var substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-?][^}]*)?"
|
||||
|
||||
var patternString = fmt.Sprintf(
|
||||
"%s(?i:(?P<escaped>%s)|(?P<named>%s)|{(?P<braced>%s)}|(?P<invalid>))",
|
||||
delimiter, delimiter, substitutionNamed, substitutionBraced,
|
||||
)
|
||||
|
||||
var defaultPattern = regexp.MustCompile(patternString)
|
||||
|
||||
// DefaultSubstituteFuncs contains the default SubstituteFunc used by the docker cli
|
||||
var DefaultSubstituteFuncs = []SubstituteFunc{
|
||||
softDefault,
|
||||
hardDefault,
|
||||
requiredNonEmpty,
|
||||
required,
|
||||
}
|
||||
|
||||
// InvalidTemplateError is returned when a variable template is not in a valid
|
||||
// format
|
||||
type InvalidTemplateError struct {
|
||||
Template string
|
||||
}
|
||||
|
||||
func (e InvalidTemplateError) Error() string {
|
||||
return fmt.Sprintf("Invalid template: %#v", e.Template)
|
||||
}
|
||||
|
||||
// Mapping is a user-supplied function which maps from variable names to values.
|
||||
// Returns the value as a string and a bool indicating whether
|
||||
// the value is present, to distinguish between an empty string
|
||||
// and the absence of a value.
|
||||
type Mapping func(string) (string, bool)
|
||||
|
||||
// SubstituteFunc is a user-supplied function that apply substitution.
|
||||
// Returns the value as a string, a bool indicating if the function could apply
|
||||
// the substitution and an error.
|
||||
type SubstituteFunc func(string, Mapping) (string, bool, error)
|
||||
|
||||
// SubstituteWith substitute variables in the string with their values.
|
||||
// It accepts additional substitute function.
|
||||
func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, subsFuncs ...SubstituteFunc) (string, error) {
|
||||
var err error
|
||||
result := pattern.ReplaceAllStringFunc(template, func(substring string) string {
|
||||
matches := pattern.FindStringSubmatch(substring)
|
||||
groups := matchGroups(matches, pattern)
|
||||
if escaped := groups["escaped"]; escaped != "" {
|
||||
return escaped
|
||||
}
|
||||
|
||||
braced := false
|
||||
substitution := groups["named"]
|
||||
if substitution == "" {
|
||||
substitution = groups["braced"]
|
||||
braced = true
|
||||
}
|
||||
|
||||
if substitution == "" {
|
||||
err = &InvalidTemplateError{Template: template}
|
||||
return ""
|
||||
}
|
||||
|
||||
if braced {
|
||||
for _, f := range subsFuncs {
|
||||
var (
|
||||
value string
|
||||
applied bool
|
||||
)
|
||||
value, applied, err = f(substitution, mapping)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if !applied {
|
||||
continue
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
value, ok := mapping(substitution)
|
||||
if !ok {
|
||||
logrus.Warnf("The %q variable is not set. Defaulting to a blank string.", substitution)
|
||||
}
|
||||
return value
|
||||
})
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Substitute variables in the string with their values
|
||||
func Substitute(template string, mapping Mapping) (string, error) {
|
||||
return SubstituteWith(template, mapping, defaultPattern, DefaultSubstituteFuncs...)
|
||||
}
|
||||
|
||||
// ExtractVariables returns a map of all the variables defined in the specified
|
||||
// composefile (dict representation) and their default value if any.
|
||||
func ExtractVariables(configDict map[string]interface{}, pattern *regexp.Regexp) map[string]Variable {
|
||||
if pattern == nil {
|
||||
pattern = defaultPattern
|
||||
}
|
||||
return recurseExtract(configDict, pattern)
|
||||
}
|
||||
|
||||
func recurseExtract(value interface{}, pattern *regexp.Regexp) map[string]Variable {
|
||||
m := map[string]Variable{}
|
||||
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if values, is := extractVariable(value, pattern); is {
|
||||
for _, v := range values {
|
||||
m[v.Name] = v
|
||||
}
|
||||
}
|
||||
case map[string]interface{}:
|
||||
for _, elem := range value {
|
||||
submap := recurseExtract(elem, pattern)
|
||||
for key, value := range submap {
|
||||
m[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
case []interface{}:
|
||||
for _, elem := range value {
|
||||
if values, is := extractVariable(elem, pattern); is {
|
||||
for _, v := range values {
|
||||
m[v.Name] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
type Variable struct {
|
||||
Name string
|
||||
DefaultValue string
|
||||
Required bool
|
||||
}
|
||||
|
||||
func extractVariable(value interface{}, pattern *regexp.Regexp) ([]Variable, bool) {
|
||||
sValue, ok := value.(string)
|
||||
if !ok {
|
||||
return []Variable{}, false
|
||||
}
|
||||
matches := pattern.FindAllStringSubmatch(sValue, -1)
|
||||
if len(matches) == 0 {
|
||||
return []Variable{}, false
|
||||
}
|
||||
values := []Variable{}
|
||||
for _, match := range matches {
|
||||
groups := matchGroups(match, pattern)
|
||||
if escaped := groups["escaped"]; escaped != "" {
|
||||
continue
|
||||
}
|
||||
val := groups["named"]
|
||||
if val == "" {
|
||||
val = groups["braced"]
|
||||
}
|
||||
name := val
|
||||
var defaultValue string
|
||||
var required bool
|
||||
switch {
|
||||
case strings.Contains(val, ":?"):
|
||||
name, _ = partition(val, ":?")
|
||||
required = true
|
||||
case strings.Contains(val, "?"):
|
||||
name, _ = partition(val, "?")
|
||||
required = true
|
||||
case strings.Contains(val, ":-"):
|
||||
name, defaultValue = partition(val, ":-")
|
||||
case strings.Contains(val, "-"):
|
||||
name, defaultValue = partition(val, "-")
|
||||
}
|
||||
values = append(values, Variable{
|
||||
Name: name,
|
||||
DefaultValue: defaultValue,
|
||||
Required: required,
|
||||
})
|
||||
}
|
||||
return values, len(values) > 0
|
||||
}
|
||||
|
||||
// Soft default (fall back if unset or empty)
|
||||
func softDefault(substitution string, mapping Mapping) (string, bool, error) {
|
||||
sep := ":-"
|
||||
if !strings.Contains(substitution, sep) {
|
||||
return "", false, nil
|
||||
}
|
||||
name, defaultValue := partition(substitution, sep)
|
||||
value, ok := mapping(name)
|
||||
if !ok || value == "" {
|
||||
return defaultValue, true, nil
|
||||
}
|
||||
return value, true, nil
|
||||
}
|
||||
|
||||
// Hard default (fall back if-and-only-if empty)
|
||||
func hardDefault(substitution string, mapping Mapping) (string, bool, error) {
|
||||
sep := "-"
|
||||
if !strings.Contains(substitution, sep) {
|
||||
return "", false, nil
|
||||
}
|
||||
name, defaultValue := partition(substitution, sep)
|
||||
value, ok := mapping(name)
|
||||
if !ok {
|
||||
return defaultValue, true, nil
|
||||
}
|
||||
return value, true, nil
|
||||
}
|
||||
|
||||
func requiredNonEmpty(substitution string, mapping Mapping) (string, bool, error) {
|
||||
return withRequired(substitution, mapping, ":?", func(v string) bool { return v != "" })
|
||||
}
|
||||
|
||||
func required(substitution string, mapping Mapping) (string, bool, error) {
|
||||
return withRequired(substitution, mapping, "?", func(_ string) bool { return true })
|
||||
}
|
||||
|
||||
func withRequired(substitution string, mapping Mapping, sep string, valid func(string) bool) (string, bool, error) {
|
||||
if !strings.Contains(substitution, sep) {
|
||||
return "", false, nil
|
||||
}
|
||||
name, errorMessage := partition(substitution, sep)
|
||||
value, ok := mapping(name)
|
||||
if !ok || !valid(value) {
|
||||
return "", true, &InvalidTemplateError{
|
||||
Template: fmt.Sprintf("required variable %s is missing a value: %s", name, errorMessage),
|
||||
}
|
||||
}
|
||||
return value, true, nil
|
||||
}
|
||||
|
||||
func matchGroups(matches []string, pattern *regexp.Regexp) map[string]string {
|
||||
groups := make(map[string]string)
|
||||
for i, name := range pattern.SubexpNames()[1:] {
|
||||
groups[name] = matches[i+1]
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
// Split the string at the first occurrence of sep, and return the part before the separator,
|
||||
// and the part after the separator.
|
||||
//
|
||||
// If the separator is not found, return the string itself, followed by an empty string.
|
||||
func partition(s, sep string) (string, string) {
|
||||
if strings.Contains(s, sep) {
|
||||
parts := strings.SplitN(s, sep, 2)
|
||||
return parts[0], parts[1]
|
||||
}
|
||||
return s, ""
|
||||
}
|
307
pkg/third_party/compose-go/template/template_test.go
vendored
Executable file
307
pkg/third_party/compose-go/template/template_test.go
vendored
Executable file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
var defaults = map[string]string{
|
||||
"FOO": "first",
|
||||
"BAR": "",
|
||||
}
|
||||
|
||||
func defaultMapping(name string) (string, bool) {
|
||||
val, ok := defaults[name]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func TestEscaped(t *testing.T) {
|
||||
result, err := Substitute("$${foo}", defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("${foo}", result))
|
||||
}
|
||||
|
||||
func TestSubstituteNoMatch(t *testing.T) {
|
||||
result, err := Substitute("foo", defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, "foo", result)
|
||||
}
|
||||
|
||||
func TestInvalid(t *testing.T) {
|
||||
invalidTemplates := []string{
|
||||
"${",
|
||||
"$}",
|
||||
"${}",
|
||||
"${ }",
|
||||
"${ foo}",
|
||||
"${foo }",
|
||||
"${foo!}",
|
||||
}
|
||||
|
||||
for _, template := range invalidTemplates {
|
||||
_, err := Substitute(template, defaultMapping)
|
||||
assert.ErrorContains(t, err, "Invalid template")
|
||||
}
|
||||
}
|
||||
|
||||
// see https://github.com/docker/compose/issues/8601
|
||||
func TestNonBraced(t *testing.T) {
|
||||
substituted, err := Substitute("$FOO-bar", defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, substituted, "first-bar")
|
||||
}
|
||||
|
||||
func TestNoValueNoDefault(t *testing.T) {
|
||||
for _, template := range []string{"This ${missing} var", "This ${BAR} var"} {
|
||||
result, err := Substitute(template, defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("This var", result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueNoDefault(t *testing.T) {
|
||||
for _, template := range []string{"This $FOO var", "This ${FOO} var"} {
|
||||
result, err := Substitute(template, defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("This first var", result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoValueWithDefault(t *testing.T) {
|
||||
for _, template := range []string{"ok ${missing:-def}", "ok ${missing-def}"} {
|
||||
result, err := Substitute(template, defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("ok def", result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyValueWithSoftDefault(t *testing.T) {
|
||||
result, err := Substitute("ok ${BAR:-def}", defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("ok def", result))
|
||||
}
|
||||
|
||||
func TestValueWithSoftDefault(t *testing.T) {
|
||||
result, err := Substitute("ok ${FOO:-def}", defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("ok first", result))
|
||||
}
|
||||
|
||||
func TestEmptyValueWithHardDefault(t *testing.T) {
|
||||
result, err := Substitute("ok ${BAR-def}", defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("ok ", result))
|
||||
}
|
||||
|
||||
func TestNonAlphanumericDefault(t *testing.T) {
|
||||
result, err := Substitute("ok ${BAR:-/non:-alphanumeric}", defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("ok /non:-alphanumeric", result))
|
||||
}
|
||||
|
||||
func TestMandatoryVariableErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
template string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
template: "not ok ${UNSET_VAR:?Mandatory Variable Unset}",
|
||||
expectedError: "required variable UNSET_VAR is missing a value: Mandatory Variable Unset",
|
||||
},
|
||||
{
|
||||
template: "not ok ${BAR:?Mandatory Variable Empty}",
|
||||
expectedError: "required variable BAR is missing a value: Mandatory Variable Empty",
|
||||
},
|
||||
{
|
||||
template: "not ok ${UNSET_VAR:?}",
|
||||
expectedError: "required variable UNSET_VAR is missing a value",
|
||||
},
|
||||
{
|
||||
template: "not ok ${UNSET_VAR?Mandatory Variable Unset}",
|
||||
expectedError: "required variable UNSET_VAR is missing a value: Mandatory Variable Unset",
|
||||
},
|
||||
{
|
||||
template: "not ok ${UNSET_VAR?}",
|
||||
expectedError: "required variable UNSET_VAR is missing a value",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
_, err := Substitute(tc.template, defaultMapping)
|
||||
assert.ErrorContains(t, err, tc.expectedError)
|
||||
assert.ErrorType(t, err, reflect.TypeOf(&InvalidTemplateError{}))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultsForMandatoryVariables(t *testing.T) {
|
||||
testCases := []struct {
|
||||
template string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
template: "ok ${FOO:?err}",
|
||||
expected: "ok first",
|
||||
},
|
||||
{
|
||||
template: "ok ${FOO?err}",
|
||||
expected: "ok first",
|
||||
},
|
||||
{
|
||||
template: "ok ${BAR?err}",
|
||||
expected: "ok ",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
result, err := Substitute(tc.template, defaultMapping)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(tc.expected, result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubstituteWithCustomFunc(t *testing.T) {
|
||||
errIsMissing := func(substitution string, mapping Mapping) (string, bool, error) {
|
||||
value, found := mapping(substitution)
|
||||
if !found {
|
||||
return "", true, &InvalidTemplateError{
|
||||
Template: fmt.Sprintf("required variable %s is missing a value", substitution),
|
||||
}
|
||||
}
|
||||
return value, true, nil
|
||||
}
|
||||
|
||||
result, err := SubstituteWith("ok ${FOO}", defaultMapping, defaultPattern, errIsMissing)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("ok first", result))
|
||||
|
||||
result, err = SubstituteWith("ok ${BAR}", defaultMapping, defaultPattern, errIsMissing)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("ok ", result))
|
||||
|
||||
_, err = SubstituteWith("ok ${NOTHERE}", defaultMapping, defaultPattern, errIsMissing)
|
||||
assert.Check(t, is.ErrorContains(err, "required variable"))
|
||||
}
|
||||
|
||||
func TestExtractVariables(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
dict map[string]interface{}
|
||||
expected map[string]Variable
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
dict: map[string]interface{}{},
|
||||
expected: map[string]Variable{},
|
||||
},
|
||||
{
|
||||
name: "no-variables",
|
||||
dict: map[string]interface{}{
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: map[string]Variable{},
|
||||
},
|
||||
{
|
||||
name: "variable-without-curly-braces",
|
||||
dict: map[string]interface{}{
|
||||
"foo": "$bar",
|
||||
},
|
||||
expected: map[string]Variable{
|
||||
"bar": {Name: "bar"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "variable",
|
||||
dict: map[string]interface{}{
|
||||
"foo": "${bar}",
|
||||
},
|
||||
expected: map[string]Variable{
|
||||
"bar": {Name: "bar", DefaultValue: ""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "required-variable",
|
||||
dict: map[string]interface{}{
|
||||
"foo": "${bar?:foo}",
|
||||
},
|
||||
expected: map[string]Variable{
|
||||
"bar": {Name: "bar", DefaultValue: "", Required: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "required-variable2",
|
||||
dict: map[string]interface{}{
|
||||
"foo": "${bar?foo}",
|
||||
},
|
||||
expected: map[string]Variable{
|
||||
"bar": {Name: "bar", DefaultValue: "", Required: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default-variable",
|
||||
dict: map[string]interface{}{
|
||||
"foo": "${bar:-foo}",
|
||||
},
|
||||
expected: map[string]Variable{
|
||||
"bar": {Name: "bar", DefaultValue: "foo"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default-variable2",
|
||||
dict: map[string]interface{}{
|
||||
"foo": "${bar-foo}",
|
||||
},
|
||||
expected: map[string]Variable{
|
||||
"bar": {Name: "bar", DefaultValue: "foo"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple-values",
|
||||
dict: map[string]interface{}{
|
||||
"foo": "${bar:-foo}",
|
||||
"bar": map[string]interface{}{
|
||||
"foo": "${fruit:-banana}",
|
||||
"bar": "vegetable",
|
||||
},
|
||||
"baz": []interface{}{
|
||||
"foo",
|
||||
"$docker:${project:-cli}",
|
||||
"$toto",
|
||||
},
|
||||
},
|
||||
expected: map[string]Variable{
|
||||
"bar": {Name: "bar", DefaultValue: "foo"},
|
||||
"fruit": {Name: "fruit", DefaultValue: "banana"},
|
||||
"toto": {Name: "toto", DefaultValue: ""},
|
||||
"docker": {Name: "docker", DefaultValue: ""},
|
||||
"project": {Name: "project", DefaultValue: "cli"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual := ExtractVariables(tc.dict, defaultPattern)
|
||||
assert.Check(t, is.DeepEqual(actual, tc.expected))
|
||||
})
|
||||
}
|
||||
}
|
105
pkg/third_party/compose-go/types/config.go
vendored
Executable file
105
pkg/third_party/compose-go/types/config.go
vendored
Executable file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// ConfigDetails are the details about a group of ConfigFiles
|
||||
type ConfigDetails struct {
|
||||
Version string
|
||||
WorkingDir string
|
||||
ConfigFiles []ConfigFile
|
||||
Environment map[string]string
|
||||
}
|
||||
|
||||
// LookupEnv provides a lookup function for environment variables
|
||||
func (cd ConfigDetails) LookupEnv(key string) (string, bool) {
|
||||
v, ok := cd.Environment[key]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// ConfigFile is a filename and the contents of the file as a Dict
|
||||
type ConfigFile struct {
|
||||
// Filename is the name of the yaml configuration file
|
||||
Filename string
|
||||
// Content is the raw yaml content. Will be loaded from Filename if not set
|
||||
Content []byte
|
||||
// Config if the yaml tree for this config file. Will be parsed from Content if not set
|
||||
Config map[string]interface{}
|
||||
}
|
||||
|
||||
// Config is a full compose file configuration and model
|
||||
type Config struct {
|
||||
Filename string `yaml:"-" json:"-"`
|
||||
Services Services `json:"services"`
|
||||
Networks Networks `yaml:",omitempty" json:"networks,omitempty"`
|
||||
Volumes Volumes `yaml:",omitempty" json:"volumes,omitempty"`
|
||||
Secrets Secrets `yaml:",omitempty" json:"secrets,omitempty"`
|
||||
Configs Configs `yaml:",omitempty" json:"configs,omitempty"`
|
||||
Extensions Extensions `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// Volumes is a map of VolumeConfig
|
||||
type Volumes map[string]VolumeConfig
|
||||
|
||||
// Networks is a map of NetworkConfig
|
||||
type Networks map[string]NetworkConfig
|
||||
|
||||
// Secrets is a map of SecretConfig
|
||||
type Secrets map[string]SecretConfig
|
||||
|
||||
// Configs is a map of ConfigObjConfig
|
||||
type Configs map[string]ConfigObjConfig
|
||||
|
||||
// Extensions is a map of custom extension
|
||||
type Extensions map[string]interface{}
|
||||
|
||||
// MarshalJSON makes Config implement json.Marshaler
|
||||
func (c Config) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{
|
||||
"services": c.Services,
|
||||
}
|
||||
|
||||
if len(c.Networks) > 0 {
|
||||
m["networks"] = c.Networks
|
||||
}
|
||||
if len(c.Volumes) > 0 {
|
||||
m["volumes"] = c.Volumes
|
||||
}
|
||||
if len(c.Secrets) > 0 {
|
||||
m["secrets"] = c.Secrets
|
||||
}
|
||||
if len(c.Configs) > 0 {
|
||||
m["configs"] = c.Configs
|
||||
}
|
||||
for k, v := range c.Extensions {
|
||||
m[k] = v
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (e Extensions) Get(name string, target interface{}) (bool, error) {
|
||||
if v, ok := e[name]; ok {
|
||||
err := mapstructure.Decode(v, target)
|
||||
return true, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
55
pkg/third_party/compose-go/types/config_test.go
vendored
Executable file
55
pkg/third_party/compose-go/types/config_test.go
vendored
Executable file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func Test_WithServices(t *testing.T) {
|
||||
p := Project{
|
||||
Services: append(Services{},
|
||||
ServiceConfig{
|
||||
Name: "service_1",
|
||||
DependsOn: map[string]ServiceDependency{
|
||||
"service_3": {
|
||||
Condition: ServiceConditionStarted,
|
||||
},
|
||||
},
|
||||
}, ServiceConfig{
|
||||
Name: "service_2",
|
||||
}, ServiceConfig{
|
||||
Name: "service_3",
|
||||
DependsOn: map[string]ServiceDependency{
|
||||
"service_2": {
|
||||
Condition: ServiceConditionStarted,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
order := []string{}
|
||||
fn := func(service ServiceConfig) error {
|
||||
order = append(order, service.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := p.WithServices(nil, fn)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, order, []string{"service_2", "service_3", "service_1"})
|
||||
}
|
344
pkg/third_party/compose-go/types/project.go
vendored
Executable file
344
pkg/third_party/compose-go/types/project.go
vendored
Executable file
@ -0,0 +1,344 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/distribution/distribution/v3/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// Project is the result of loading a set of compose files
|
||||
type Project struct {
|
||||
Name string `yaml:"-" json:"-"`
|
||||
WorkingDir string `yaml:"-" json:"-"`
|
||||
Services Services `json:"services"`
|
||||
Networks Networks `yaml:",omitempty" json:"networks,omitempty"`
|
||||
Volumes Volumes `yaml:",omitempty" json:"volumes,omitempty"`
|
||||
Secrets Secrets `yaml:",omitempty" json:"secrets,omitempty"`
|
||||
Configs Configs `yaml:",omitempty" json:"configs,omitempty"`
|
||||
Extensions Extensions `yaml:",inline" json:"-"` // https://github.com/golang/go/issues/6213
|
||||
ComposeFiles []string `yaml:"-" json:"-"`
|
||||
Environment map[string]string `yaml:"-" json:"-"`
|
||||
|
||||
// DisabledServices track services which have been disable as profile is not active
|
||||
DisabledServices Services `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
// ServiceNames return names for all services in this Compose config
|
||||
func (p Project) ServiceNames() []string {
|
||||
names := []string{}
|
||||
for _, s := range p.Services {
|
||||
names = append(names, s.Name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// VolumeNames return names for all volumes in this Compose config
|
||||
func (p Project) VolumeNames() []string {
|
||||
names := []string{}
|
||||
for k := range p.Volumes {
|
||||
names = append(names, k)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// NetworkNames return names for all volumes in this Compose config
|
||||
func (p Project) NetworkNames() []string {
|
||||
names := []string{}
|
||||
for k := range p.Networks {
|
||||
names = append(names, k)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// SecretNames return names for all secrets in this Compose config
|
||||
func (p Project) SecretNames() []string {
|
||||
names := []string{}
|
||||
for k := range p.Secrets {
|
||||
names = append(names, k)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// ConfigNames return names for all configs in this Compose config
|
||||
func (p Project) ConfigNames() []string {
|
||||
names := []string{}
|
||||
for k := range p.Configs {
|
||||
names = append(names, k)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// GetServices retrieve services by names, or return all services if no name specified
|
||||
func (p Project) GetServices(names ...string) (Services, error) {
|
||||
if len(names) == 0 {
|
||||
return p.Services, nil
|
||||
}
|
||||
services := Services{}
|
||||
for _, name := range names {
|
||||
var serviceConfig *ServiceConfig
|
||||
for _, s := range p.Services {
|
||||
if s.Name == name {
|
||||
serviceConfig = &s
|
||||
break
|
||||
}
|
||||
}
|
||||
if serviceConfig == nil {
|
||||
return services, fmt.Errorf("no such service: %s", name)
|
||||
}
|
||||
services = append(services, *serviceConfig)
|
||||
}
|
||||
return services, nil
|
||||
}
|
||||
|
||||
// GetService retrieve a specific service by name
|
||||
func (p Project) GetService(name string) (ServiceConfig, error) {
|
||||
services, err := p.GetServices(name)
|
||||
if err != nil {
|
||||
return ServiceConfig{}, err
|
||||
}
|
||||
if len(services) == 0 {
|
||||
return ServiceConfig{}, fmt.Errorf("no such service: %s", name)
|
||||
}
|
||||
return services[0], nil
|
||||
}
|
||||
|
||||
func (p Project) AllServices() Services {
|
||||
var all Services
|
||||
all = append(all, p.Services...)
|
||||
all = append(all, p.DisabledServices...)
|
||||
return all
|
||||
}
|
||||
|
||||
type ServiceFunc func(service ServiceConfig) error
|
||||
|
||||
// WithServices run ServiceFunc on each service and dependencies in dependency order
|
||||
func (p Project) WithServices(names []string, fn ServiceFunc) error {
|
||||
return p.withServices(names, fn, map[string]bool{})
|
||||
}
|
||||
|
||||
func (p Project) withServices(names []string, fn ServiceFunc, done map[string]bool) error {
|
||||
services, err := p.GetServices(names...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, service := range services {
|
||||
if done[service.Name] {
|
||||
continue
|
||||
}
|
||||
//KCQ
|
||||
done[service.Name] = true
|
||||
dependencies := service.GetDependencies()
|
||||
if len(dependencies) > 0 {
|
||||
err := p.withServices(dependencies, fn, done)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := fn(service); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RelativePath resolve a relative path based project's working directory
|
||||
func (p *Project) RelativePath(path string) string {
|
||||
if path[0] == '~' {
|
||||
home, _ := os.UserHomeDir()
|
||||
path = filepath.Join(home, path[1:])
|
||||
}
|
||||
if filepath.IsAbs(path) {
|
||||
return path
|
||||
}
|
||||
return filepath.Join(p.WorkingDir, path)
|
||||
}
|
||||
|
||||
// HasProfile return true if service has no profile declared or has at least one profile matching
|
||||
func (service ServiceConfig) HasProfile(profiles []string) bool {
|
||||
if len(service.Profiles) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, p := range profiles {
|
||||
for _, sp := range service.Profiles {
|
||||
if sp == p {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetProfiles retrieve the profiles implicitly enabled by explicitly targeting selected services
|
||||
func (s Services) GetProfiles() []string {
|
||||
set := map[string]struct{}{}
|
||||
for _, service := range s {
|
||||
for _, p := range service.Profiles {
|
||||
set[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
var profiles []string
|
||||
for k := range set {
|
||||
profiles = append(profiles, k)
|
||||
}
|
||||
return profiles
|
||||
}
|
||||
|
||||
// ApplyProfiles disables service which don't match selected profiles
|
||||
func (p *Project) ApplyProfiles(profiles []string) {
|
||||
for _, p := range profiles {
|
||||
if p == "*" {
|
||||
return
|
||||
}
|
||||
}
|
||||
var enabled, disabled Services
|
||||
for _, service := range p.Services {
|
||||
if service.HasProfile(profiles) {
|
||||
enabled = append(enabled, service)
|
||||
} else {
|
||||
disabled = append(disabled, service)
|
||||
}
|
||||
}
|
||||
p.Services = enabled
|
||||
p.DisabledServices = disabled
|
||||
}
|
||||
|
||||
// WithoutUnnecessaryResources drops networks/volumes/secrets/configs that are not referenced by active services
|
||||
func (p *Project) WithoutUnnecessaryResources() {
|
||||
requiredNetworks := map[string]struct{}{}
|
||||
requiredVolumes := map[string]struct{}{}
|
||||
requiredSecrets := map[string]struct{}{}
|
||||
requiredConfigs := map[string]struct{}{}
|
||||
for _, s := range p.Services {
|
||||
for k := range s.Networks {
|
||||
requiredNetworks[k] = struct{}{}
|
||||
}
|
||||
for _, v := range s.Volumes {
|
||||
if v.Type != VolumeTypeVolume || v.Source == "" {
|
||||
continue
|
||||
}
|
||||
requiredVolumes[v.Source] = struct{}{}
|
||||
}
|
||||
for _, v := range s.Secrets {
|
||||
requiredSecrets[v.Source] = struct{}{}
|
||||
}
|
||||
for _, v := range s.Configs {
|
||||
requiredConfigs[v.Source] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
networks := Networks{}
|
||||
for k := range requiredNetworks {
|
||||
networks[k] = p.Networks[k]
|
||||
}
|
||||
p.Networks = networks
|
||||
|
||||
volumes := Volumes{}
|
||||
for k := range requiredVolumes {
|
||||
volumes[k] = p.Volumes[k]
|
||||
}
|
||||
p.Volumes = volumes
|
||||
|
||||
secrets := Secrets{}
|
||||
for k := range requiredSecrets {
|
||||
secrets[k] = p.Secrets[k]
|
||||
}
|
||||
p.Secrets = secrets
|
||||
|
||||
configs := Configs{}
|
||||
for k := range requiredConfigs {
|
||||
configs[k] = p.Configs[k]
|
||||
}
|
||||
p.Configs = configs
|
||||
}
|
||||
|
||||
// ForServices restrict the project model to a subset of services
|
||||
func (p *Project) ForServices(names []string) error {
|
||||
if len(names) == 0 {
|
||||
// All services
|
||||
return nil
|
||||
}
|
||||
|
||||
set := map[string]struct{}{}
|
||||
err := p.WithServices(names, func(service ServiceConfig) error {
|
||||
set[service.Name] = struct{}{}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Disable all services which are not explicit target or dependencies
|
||||
var enabled Services
|
||||
for _, s := range p.Services {
|
||||
if _, ok := set[s.Name]; ok {
|
||||
enabled = append(enabled, s)
|
||||
} else {
|
||||
p.DisabledServices = append(p.DisabledServices, s)
|
||||
}
|
||||
}
|
||||
p.Services = enabled
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResolveImages updates services images to include digest computed by a resolver function
|
||||
func (p *Project) ResolveImages(resolver func(named reference.Named) (digest.Digest, error)) error {
|
||||
eg := errgroup.Group{}
|
||||
for i, s := range p.Services {
|
||||
idx := i
|
||||
service := s
|
||||
|
||||
if service.Image == "" {
|
||||
continue
|
||||
}
|
||||
eg.Go(func() error {
|
||||
named, err := reference.ParseDockerRef(service.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := named.(reference.Canonical); !ok {
|
||||
// image is named but not digested reference
|
||||
digest, err := resolver(named)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
named, err = reference.WithDigest(named, digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
service.Image = named.String()
|
||||
p.Services[idx] = service
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return eg.Wait()
|
||||
}
|
144
pkg/third_party/compose-go/types/project_test.go
vendored
Executable file
144
pkg/third_party/compose-go/types/project_test.go
vendored
Executable file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
_ "crypto/sha256"
|
||||
"testing"
|
||||
|
||||
"github.com/distribution/distribution/v3/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func Test_ApplyProfiles(t *testing.T) {
|
||||
p := makeProject()
|
||||
p.ApplyProfiles([]string{"foo"})
|
||||
assert.Equal(t, len(p.Services), 2)
|
||||
assert.Equal(t, p.Services[0].Name, "service_1")
|
||||
assert.Equal(t, p.Services[1].Name, "service_2")
|
||||
assert.Equal(t, len(p.DisabledServices), 1)
|
||||
assert.Equal(t, p.DisabledServices[0].Name, "service_3")
|
||||
}
|
||||
|
||||
func Test_WithoutUnnecessaryResources(t *testing.T) {
|
||||
p := makeProject()
|
||||
p.Networks["unused"] = NetworkConfig{}
|
||||
p.Volumes["unused"] = VolumeConfig{}
|
||||
p.Secrets["unused"] = SecretConfig{}
|
||||
p.Configs["unused"] = ConfigObjConfig{}
|
||||
p.WithoutUnnecessaryResources()
|
||||
if _, ok := p.Networks["unused"]; ok {
|
||||
t.Fail()
|
||||
}
|
||||
if _, ok := p.Volumes["unused"]; ok {
|
||||
t.Fail()
|
||||
}
|
||||
if _, ok := p.Secrets["unused"]; ok {
|
||||
t.Fail()
|
||||
}
|
||||
if _, ok := p.Configs["unused"]; ok {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NoProfiles(t *testing.T) {
|
||||
p := makeProject()
|
||||
p.ApplyProfiles(nil)
|
||||
assert.Equal(t, len(p.Services), 1)
|
||||
assert.Equal(t, len(p.DisabledServices), 2)
|
||||
assert.Equal(t, p.Services[0].Name, "service_1")
|
||||
}
|
||||
|
||||
func Test_ServiceProfiles(t *testing.T) {
|
||||
p := makeProject()
|
||||
services, err := p.GetServices("service_1", "service_2")
|
||||
assert.NilError(t, err)
|
||||
|
||||
profiles := services.GetProfiles()
|
||||
assert.Equal(t, len(profiles), 1)
|
||||
assert.Equal(t, profiles[0], "foo")
|
||||
}
|
||||
|
||||
func Test_ForServices(t *testing.T) {
|
||||
p := makeProject()
|
||||
err := p.ForServices([]string{"service_2"})
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, len(p.DisabledServices), 1)
|
||||
assert.Equal(t, p.DisabledServices[0].Name, "service_3")
|
||||
}
|
||||
|
||||
func makeProject() Project {
|
||||
return Project{
|
||||
Services: append(Services{},
|
||||
ServiceConfig{
|
||||
Name: "service_1",
|
||||
}, ServiceConfig{
|
||||
Name: "service_2",
|
||||
Profiles: []string{"foo"},
|
||||
DependsOn: map[string]ServiceDependency{"service_1": {}},
|
||||
}, ServiceConfig{
|
||||
Name: "service_3",
|
||||
Profiles: []string{"bar"},
|
||||
}),
|
||||
Networks: Networks{},
|
||||
Volumes: Volumes{},
|
||||
Secrets: Secrets{},
|
||||
Configs: Configs{},
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ResolveImages(t *testing.T) {
|
||||
p := makeProject()
|
||||
resolver := func(named reference.Named) (digest.Digest, error) {
|
||||
return "sha256:1234567890123456789012345678901234567890123456789012345678901234", nil
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
image string
|
||||
resolved string
|
||||
}{
|
||||
{
|
||||
image: "com.acme/long:tag",
|
||||
resolved: "com.acme/long:tag@sha256:1234567890123456789012345678901234567890123456789012345678901234",
|
||||
},
|
||||
{
|
||||
image: "com.acme/long",
|
||||
resolved: "com.acme/long:latest@sha256:1234567890123456789012345678901234567890123456789012345678901234",
|
||||
},
|
||||
{
|
||||
image: "short",
|
||||
resolved: "docker.io/library/short:latest@sha256:1234567890123456789012345678901234567890123456789012345678901234",
|
||||
},
|
||||
{
|
||||
image: "com.acme/digested:tag@sha256:1234567890123456789012345678901234567890123456789012345678901234",
|
||||
resolved: "com.acme/digested@sha256:1234567890123456789012345678901234567890123456789012345678901234",
|
||||
},
|
||||
{
|
||||
image: "com.acme/digested@sha256:1234567890123456789012345678901234567890123456789012345678901234",
|
||||
resolved: "com.acme/digested@sha256:1234567890123456789012345678901234567890123456789012345678901234",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
p.Services[0].Image = test.image
|
||||
err := p.ResolveImages(resolver)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, p.Services[0].Image, test.resolved)
|
||||
}
|
||||
}
|
849
pkg/third_party/compose-go/types/types.go
vendored
Executable file
849
pkg/third_party/compose-go/types/types.go
vendored
Executable file
@ -0,0 +1,849 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
// Duration is a thin wrapper around time.Duration with improved JSON marshalling
|
||||
type Duration time.Duration
|
||||
|
||||
func (d Duration) String() string {
|
||||
return time.Duration(d).String()
|
||||
}
|
||||
|
||||
// ConvertDurationPtr converts a typedefined Duration pointer to a time.Duration pointer with the same value.
|
||||
func ConvertDurationPtr(d *Duration) *time.Duration {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
res := time.Duration(*d)
|
||||
return &res
|
||||
}
|
||||
|
||||
// MarshalJSON makes Duration implement json.Marshaler
|
||||
func (d Duration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(d.String())
|
||||
}
|
||||
|
||||
// MarshalYAML makes Duration implement yaml.Marshaler
|
||||
func (d Duration) MarshalYAML() (interface{}, error) {
|
||||
return d.String(), nil
|
||||
}
|
||||
|
||||
func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||
s := strings.Trim(string(b), "\"")
|
||||
timeDuration, err := time.ParseDuration(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = Duration(timeDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Services is a list of ServiceConfig
|
||||
type Services []ServiceConfig
|
||||
|
||||
// MarshalYAML makes Services implement yaml.Marshaller
|
||||
func (s Services) MarshalYAML() (interface{}, error) {
|
||||
services := map[string]ServiceConfig{}
|
||||
for _, service := range s {
|
||||
services[service.Name] = service
|
||||
}
|
||||
return services, nil
|
||||
}
|
||||
|
||||
// MarshalJSON makes Services implement json.Marshaler
|
||||
func (s Services) MarshalJSON() ([]byte, error) {
|
||||
data, err := s.MarshalYAML()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.MarshalIndent(data, "", " ")
|
||||
}
|
||||
|
||||
// ServiceConfig is the configuration of one service
|
||||
type ServiceConfig struct {
|
||||
Name string `yaml:"-" json:"-"`
|
||||
Profiles []string `mapstructure:"profiles" yaml:"profiles,omitempty" json:"profiles,omitempty"`
|
||||
|
||||
Build *BuildConfig `yaml:",omitempty" json:"build,omitempty"`
|
||||
BlkioConfig *BlkioConfig `yaml:",omitempty" json:"blkio_config,omitempty"`
|
||||
CapAdd []string `mapstructure:"cap_add" yaml:"cap_add,omitempty" json:"cap_add,omitempty"`
|
||||
CapDrop []string `mapstructure:"cap_drop" yaml:"cap_drop,omitempty" json:"cap_drop,omitempty"`
|
||||
CgroupParent string `mapstructure:"cgroup_parent" yaml:"cgroup_parent,omitempty" json:"cgroup_parent,omitempty"`
|
||||
CPUCount int64 `mapstructure:"cpu_count" yaml:"cpu_count,omitempty" json:"cpu_count,omitempty"`
|
||||
CPUPercent float32 `mapstructure:"cpu_percent" yaml:"cpu_percent,omitempty" json:"cpu_percent,omitempty"`
|
||||
CPUPeriod int64 `mapstructure:"cpu_period" yaml:"cpu_period,omitempty" json:"cpu_period,omitempty"`
|
||||
CPUQuota int64 `mapstructure:"cpu_quota" yaml:"cpu_quota,omitempty" json:"cpu_quota,omitempty"`
|
||||
CPURTPeriod int64 `mapstructure:"cpu_rt_period" yaml:"cpu_rt_period,omitempty" json:"cpu_rt_period,omitempty"`
|
||||
CPURTRuntime int64 `mapstructure:"cpu_rt_runtime" yaml:"cpu_rt_runtime,omitempty" json:"cpu_rt_runtime,omitempty"`
|
||||
CPUS float32 `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"`
|
||||
CPUSet string `mapstructure:"cpuset" yaml:"cpuset,omitempty" json:"cpuset,omitempty"`
|
||||
CPUShares int64 `mapstructure:"cpu_shares" yaml:"cpu_shares,omitempty" json:"cpu_shares,omitempty"`
|
||||
Command ShellCommand `yaml:",omitempty" json:"command,omitempty"`
|
||||
Configs []ServiceConfigObjConfig `yaml:",omitempty" json:"configs,omitempty"`
|
||||
ContainerName string `mapstructure:"container_name" yaml:"container_name,omitempty" json:"container_name,omitempty"`
|
||||
CredentialSpec *CredentialSpecConfig `mapstructure:"credential_spec" yaml:"credential_spec,omitempty" json:"credential_spec,omitempty"`
|
||||
DependsOn DependsOnConfig `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"`
|
||||
Deploy *DeployConfig `yaml:",omitempty" json:"deploy,omitempty"`
|
||||
Devices []string `yaml:",omitempty" json:"devices,omitempty"`
|
||||
DNS StringList `yaml:",omitempty" json:"dns,omitempty"`
|
||||
DNSOpts []string `mapstructure:"dns_opt" yaml:"dns_opt,omitempty" json:"dns_opt,omitempty"`
|
||||
DNSSearch StringList `mapstructure:"dns_search" yaml:"dns_search,omitempty" json:"dns_search,omitempty"`
|
||||
Dockerfile string `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"`
|
||||
DomainName string `mapstructure:"domainname" yaml:"domainname,omitempty" json:"domainname,omitempty"`
|
||||
Entrypoint ShellCommand `yaml:",omitempty" json:"entrypoint,omitempty"`
|
||||
Environment MappingWithEquals `yaml:",omitempty" json:"environment,omitempty"`
|
||||
EnvFile StringList `mapstructure:"env_file" yaml:"env_file,omitempty" json:"env_file,omitempty"`
|
||||
Expose StringOrNumberList `yaml:",omitempty" json:"expose,omitempty"`
|
||||
Extends ExtendsConfig `yaml:"extends,omitempty" json:"extends,omitempty"`
|
||||
ExternalLinks []string `mapstructure:"external_links" yaml:"external_links,omitempty" json:"external_links,omitempty"`
|
||||
ExtraHosts HostsList `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"`
|
||||
GroupAdd []string `mapstructure:"group_app" yaml:"group_add,omitempty" json:"group_add,omitempty"`
|
||||
Hostname string `yaml:",omitempty" json:"hostname,omitempty"`
|
||||
HealthCheck *HealthCheckConfig `yaml:",omitempty" json:"healthcheck,omitempty"`
|
||||
Image string `yaml:",omitempty" json:"image,omitempty"`
|
||||
Init *bool `yaml:",omitempty" json:"init,omitempty"`
|
||||
Ipc string `yaml:",omitempty" json:"ipc,omitempty"`
|
||||
Isolation string `mapstructure:"isolation" yaml:"isolation,omitempty" json:"isolation,omitempty"`
|
||||
Labels Labels `yaml:",omitempty" json:"labels,omitempty"`
|
||||
Links []string `yaml:",omitempty" json:"links,omitempty"`
|
||||
Logging *LoggingConfig `yaml:",omitempty" json:"logging,omitempty"`
|
||||
LogDriver string `mapstructure:"log_driver" yaml:"log_driver,omitempty" json:"log_driver,omitempty"`
|
||||
LogOpt map[string]string `mapstructure:"log_opt" yaml:"log_opt,omitempty" json:"log_opt,omitempty"`
|
||||
MemLimit UnitBytes `mapstructure:"mem_limit" yaml:"mem_limit,omitempty" json:"mem_limit,omitempty"`
|
||||
MemReservation UnitBytes `mapstructure:"mem_reservation" yaml:"mem_reservation,omitempty" json:"mem_reservation,omitempty"`
|
||||
MemSwapLimit UnitBytes `mapstructure:"memswap_limit" yaml:"memswap_limit,omitempty" json:"memswap_limit,omitempty"`
|
||||
MemSwappiness UnitBytes `mapstructure:"mem_swappiness" yaml:"mem_swappiness,omitempty" json:"mem_swappiness,omitempty"`
|
||||
MacAddress string `mapstructure:"mac_address" yaml:"mac_address,omitempty" json:"mac_address,omitempty"`
|
||||
Net string `yaml:"net,omitempty" json:"net,omitempty"`
|
||||
NetworkMode string `mapstructure:"network_mode" yaml:"network_mode,omitempty" json:"network_mode,omitempty"`
|
||||
Networks map[string]*ServiceNetworkConfig `yaml:",omitempty" json:"networks,omitempty"`
|
||||
OomKillDisable bool `mapstructure:"oom_kill_disable" yaml:"oom_kill_disable,omitempty" json:"oom_kill_disable,omitempty"`
|
||||
OomScoreAdj int64 `mapstructure:"oom_score_adj" yaml:"oom_score_adj,omitempty" json:"oom_score_adj,omitempty"`
|
||||
Pid string `yaml:",omitempty" json:"pid,omitempty"`
|
||||
PidsLimit int64 `mapstructure:"pids_limit" yaml:"pids_limit,omitempty" json:"pids_limit,omitempty"`
|
||||
Platform string `yaml:",omitempty" json:"platform,omitempty"`
|
||||
Ports []ServicePortConfig `yaml:",omitempty" json:"ports,omitempty"`
|
||||
Privileged bool `yaml:",omitempty" json:"privileged,omitempty"`
|
||||
PullPolicy string `mapstructure:"pull_policy" yaml:"pull_policy,omitempty" json:"pull_policy,omitempty"`
|
||||
ReadOnly bool `mapstructure:"read_only" yaml:"read_only,omitempty" json:"read_only,omitempty"`
|
||||
Restart string `yaml:",omitempty" json:"restart,omitempty"`
|
||||
Runtime string `yaml:",omitempty" json:"runtime,omitempty"`
|
||||
Scale int `yaml:"-" json:"-"`
|
||||
Secrets []ServiceSecretConfig `yaml:",omitempty" json:"secrets,omitempty"`
|
||||
SecurityOpt []string `mapstructure:"security_opt" yaml:"security_opt,omitempty" json:"security_opt,omitempty"`
|
||||
ShmSize UnitBytes `mapstructure:"shm_size" yaml:"shm_size,omitempty" json:"shm_size,omitempty"`
|
||||
StdinOpen bool `mapstructure:"stdin_open" yaml:"stdin_open,omitempty" json:"stdin_open,omitempty"`
|
||||
StopGracePeriod *Duration `mapstructure:"stop_grace_period" yaml:"stop_grace_period,omitempty" json:"stop_grace_period,omitempty"`
|
||||
StopSignal string `mapstructure:"stop_signal" yaml:"stop_signal,omitempty" json:"stop_signal,omitempty"`
|
||||
Sysctls Mapping `yaml:",omitempty" json:"sysctls,omitempty"`
|
||||
Tmpfs StringList `yaml:",omitempty" json:"tmpfs,omitempty"`
|
||||
Tty bool `mapstructure:"tty" yaml:"tty,omitempty" json:"tty,omitempty"`
|
||||
Ulimits map[string]*UlimitsConfig `yaml:",omitempty" json:"ulimits,omitempty"`
|
||||
User string `yaml:",omitempty" json:"user,omitempty"`
|
||||
UserNSMode string `mapstructure:"userns_mode" yaml:"userns_mode,omitempty" json:"userns_mode,omitempty"`
|
||||
Uts string `yaml:"uts,omitempty" json:"uts,omitempty"`
|
||||
VolumeDriver string `mapstructure:"volume_driver" yaml:"volume_driver,omitempty" json:"volume_driver,omitempty"`
|
||||
Volumes []ServiceVolumeConfig `yaml:",omitempty" json:"volumes,omitempty"`
|
||||
VolumesFrom []string `mapstructure:"volumes_from" yaml:"volumes_from,omitempty" json:"volumes_from,omitempty"`
|
||||
WorkingDir string `mapstructure:"working_dir" yaml:"working_dir,omitempty" json:"working_dir,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// NetworksByPriority return the service networks IDs sorted according to Priority
|
||||
func (s *ServiceConfig) NetworksByPriority() []string {
|
||||
type key struct {
|
||||
name string
|
||||
priority int
|
||||
}
|
||||
var keys []key
|
||||
for k, v := range s.Networks {
|
||||
priority := 0
|
||||
if v != nil {
|
||||
priority = v.Priority
|
||||
}
|
||||
keys = append(keys, key{
|
||||
name: k,
|
||||
priority: priority,
|
||||
})
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i].priority > keys[j].priority
|
||||
})
|
||||
var sorted []string
|
||||
for _, k := range keys {
|
||||
sorted = append(sorted, k.name)
|
||||
}
|
||||
return sorted
|
||||
}
|
||||
|
||||
const (
|
||||
//PullPolicyAlways always pull images
|
||||
PullPolicyAlways = "always"
|
||||
//PullPolicyNever never pull images
|
||||
PullPolicyNever = "never"
|
||||
//PullPolicyIfNotPresent pull missing images
|
||||
PullPolicyIfNotPresent = "if_not_present"
|
||||
//PullPolicyIfNotPresent pull missing images
|
||||
PullPolicyMissing = "missing"
|
||||
//PullPolicyBuild force building images
|
||||
PullPolicyBuild = "build"
|
||||
)
|
||||
|
||||
const (
|
||||
//RestartPolicyAlways always restart the container if it stops
|
||||
RestartPolicyAlways = "always"
|
||||
//RestartPolicyOnFailure restart the container if it exits due to an error
|
||||
RestartPolicyOnFailure = "on-failure"
|
||||
//RestartPolicyNo do not automatically restart the container
|
||||
RestartPolicyNo = "no"
|
||||
//RestartPolicyUnlessStopped always restart the container unless the container is stopped (manually or otherwise)
|
||||
RestartPolicyUnlessStopped = "unless-stopped"
|
||||
)
|
||||
|
||||
const (
|
||||
// ServicePrefix is the prefix for references pointing to a service
|
||||
ServicePrefix = "service:"
|
||||
// ContainerPrefix is the prefix for references pointing to a container
|
||||
ContainerPrefix = "container:"
|
||||
|
||||
// NetworkModeServicePrefix is the prefix for network_mode pointing to a service
|
||||
// Deprecated prefer ServicePrefix
|
||||
NetworkModeServicePrefix = ServicePrefix
|
||||
// NetworkModeContainerPrefix is the prefix for network_mode pointing to a container
|
||||
// Deprecated prefer ContainerPrefix
|
||||
NetworkModeContainerPrefix = ContainerPrefix
|
||||
)
|
||||
|
||||
// GetDependencies retrieve all services this service depends on
|
||||
func (s ServiceConfig) GetDependencies() []string {
|
||||
dependencies := make(set)
|
||||
for dependency := range s.DependsOn {
|
||||
dependencies.append(dependency)
|
||||
}
|
||||
for _, link := range s.Links {
|
||||
parts := strings.Split(link, ":")
|
||||
if len(parts) == 2 {
|
||||
dependencies.append(parts[0])
|
||||
} else {
|
||||
dependencies.append(link)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(s.NetworkMode, ServicePrefix) {
|
||||
dependencies.append(s.NetworkMode[len(ServicePrefix):])
|
||||
}
|
||||
if strings.HasPrefix(s.Ipc, ServicePrefix) {
|
||||
dependencies.append(s.Ipc[len(ServicePrefix):])
|
||||
}
|
||||
if strings.HasPrefix(s.Pid, ServicePrefix) {
|
||||
dependencies.append(s.Pid[len(ServicePrefix):])
|
||||
}
|
||||
for _, vol := range s.VolumesFrom {
|
||||
if !strings.HasPrefix(s.Pid, ContainerPrefix) {
|
||||
dependencies.append(vol)
|
||||
}
|
||||
}
|
||||
|
||||
return dependencies.toSlice()
|
||||
}
|
||||
|
||||
type set map[string]struct{}
|
||||
|
||||
func (s set) append(strings ...string) {
|
||||
for _, str := range strings {
|
||||
s[str] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (s set) toSlice() []string {
|
||||
slice := make([]string, 0, len(s))
|
||||
for v := range s {
|
||||
slice = append(slice, v)
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
// BuildConfig is a type for build
|
||||
type BuildConfig struct {
|
||||
Context string `yaml:",omitempty" json:"context,omitempty"`
|
||||
Dockerfile string `yaml:",omitempty" json:"dockerfile,omitempty"`
|
||||
Args MappingWithEquals `yaml:",omitempty" json:"args,omitempty"`
|
||||
Labels Labels `yaml:",omitempty" json:"labels,omitempty"`
|
||||
CacheFrom StringList `mapstructure:"cache_from" yaml:"cache_from,omitempty" json:"cache_from,omitempty"`
|
||||
ExtraHosts HostsList `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"`
|
||||
Isolation string `yaml:",omitempty" json:"isolation,omitempty"`
|
||||
Network string `yaml:",omitempty" json:"network,omitempty"`
|
||||
Target string `yaml:",omitempty" json:"target,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// BlkioConfig define blkio config
|
||||
type BlkioConfig struct {
|
||||
Weight uint16 `yaml:",omitempty" json:"weight,omitempty"`
|
||||
WeightDevice []WeightDevice `yaml:",omitempty" json:"weight_device,omitempty"`
|
||||
DeviceReadBps []ThrottleDevice `yaml:",omitempty" json:"device_read_bps,omitempty"`
|
||||
DeviceReadIOps []ThrottleDevice `yaml:",omitempty" json:"device_read_iops,omitempty"`
|
||||
DeviceWriteBps []ThrottleDevice `yaml:",omitempty" json:"device_write_bps,omitempty"`
|
||||
DeviceWriteIOps []ThrottleDevice `yaml:",omitempty" json:"device_write_iops,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// WeightDevice is a structure that holds device:weight pair
|
||||
type WeightDevice struct {
|
||||
Path string
|
||||
Weight uint16
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// ThrottleDevice is a structure that holds device:rate_per_second pair
|
||||
type ThrottleDevice struct {
|
||||
Path string
|
||||
Rate uint64
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// ShellCommand is a string or list of string args
|
||||
type ShellCommand []string
|
||||
|
||||
// StringList is a type for fields that can be a string or list of strings
|
||||
type StringList []string
|
||||
|
||||
// StringOrNumberList is a type for fields that can be a list of strings or
|
||||
// numbers
|
||||
type StringOrNumberList []string
|
||||
|
||||
// MappingWithEquals is a mapping type that can be converted from a list of
|
||||
// key[=value] strings.
|
||||
// For the key with an empty value (`key=`), the mapped value is set to a pointer to `""`.
|
||||
// For the key without value (`key`), the mapped value is set to nil.
|
||||
type MappingWithEquals map[string]*string
|
||||
|
||||
// NewMappingWithEquals build a new Mapping from a set of KEY=VALUE strings
|
||||
func NewMappingWithEquals(values []string) MappingWithEquals {
|
||||
mapping := MappingWithEquals{}
|
||||
for _, env := range values {
|
||||
tokens := strings.SplitN(env, "=", 2)
|
||||
if len(tokens) > 1 {
|
||||
mapping[tokens[0]] = &tokens[1]
|
||||
} else {
|
||||
mapping[env] = nil
|
||||
}
|
||||
}
|
||||
return mapping
|
||||
}
|
||||
|
||||
// OverrideBy update MappingWithEquals with values from another MappingWithEquals
|
||||
func (e MappingWithEquals) OverrideBy(other MappingWithEquals) MappingWithEquals {
|
||||
for k, v := range other {
|
||||
e[k] = v
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Resolve update a MappingWithEquals for keys without value (`key`, but not `key=`)
|
||||
func (e MappingWithEquals) Resolve(lookupFn func(string) (string, bool)) MappingWithEquals {
|
||||
for k, v := range e {
|
||||
if v == nil {
|
||||
if value, ok := lookupFn(k); ok {
|
||||
e[k] = &value
|
||||
}
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// RemoveEmpty excludes keys that are not associated with a value
|
||||
func (e MappingWithEquals) RemoveEmpty() MappingWithEquals {
|
||||
for k, v := range e {
|
||||
if v == nil {
|
||||
delete(e, k)
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Mapping is a mapping type that can be converted from a list of
|
||||
// key[=value] strings.
|
||||
// For the key with an empty value (`key=`), or key without value (`key`), the
|
||||
// mapped value is set to an empty string `""`.
|
||||
type Mapping map[string]string
|
||||
|
||||
// NewMapping build a new Mapping from a set of KEY=VALUE strings
|
||||
func NewMapping(values []string) Mapping {
|
||||
mapping := Mapping{}
|
||||
for _, value := range values {
|
||||
parts := strings.SplitN(value, "=", 2)
|
||||
key := parts[0]
|
||||
switch {
|
||||
case len(parts) == 1:
|
||||
mapping[key] = ""
|
||||
default:
|
||||
mapping[key] = parts[1]
|
||||
}
|
||||
}
|
||||
return mapping
|
||||
}
|
||||
|
||||
// Labels is a mapping type for labels
|
||||
type Labels map[string]string
|
||||
|
||||
func (l Labels) Add(key, value string) Labels {
|
||||
if l == nil {
|
||||
l = Labels{}
|
||||
}
|
||||
l[key] = value
|
||||
return l
|
||||
}
|
||||
|
||||
// MappingWithColon is a mapping type that can be converted from a list of
|
||||
// 'key: value' strings
|
||||
type MappingWithColon map[string]string
|
||||
|
||||
// HostsList is a list of colon-separated host-ip mappings
|
||||
type HostsList []string
|
||||
|
||||
// LoggingConfig the logging configuration for a service
|
||||
type LoggingConfig struct {
|
||||
Driver string `yaml:",omitempty" json:"driver,omitempty"`
|
||||
Options map[string]string `yaml:",omitempty" json:"options,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// DeployConfig the deployment configuration for a service
|
||||
type DeployConfig struct {
|
||||
Mode string `yaml:",omitempty" json:"mode,omitempty"`
|
||||
Replicas *uint64 `yaml:",omitempty" json:"replicas,omitempty"`
|
||||
Labels Labels `yaml:",omitempty" json:"labels,omitempty"`
|
||||
UpdateConfig *UpdateConfig `mapstructure:"update_config" yaml:"update_config,omitempty" json:"update_config,omitempty"`
|
||||
RollbackConfig *UpdateConfig `mapstructure:"rollback_config" yaml:"rollback_config,omitempty" json:"rollback_config,omitempty"`
|
||||
Resources Resources `yaml:",omitempty" json:"resources,omitempty"`
|
||||
RestartPolicy *RestartPolicy `mapstructure:"restart_policy" yaml:"restart_policy,omitempty" json:"restart_policy,omitempty"`
|
||||
Placement Placement `yaml:",omitempty" json:"placement,omitempty"`
|
||||
EndpointMode string `mapstructure:"endpoint_mode" yaml:"endpoint_mode,omitempty" json:"endpoint_mode,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// HealthCheckConfig the healthcheck configuration for a service
|
||||
type HealthCheckConfig struct {
|
||||
Test HealthCheckTest `yaml:",omitempty" json:"test,omitempty"`
|
||||
Timeout *Duration `yaml:",omitempty" json:"timeout,omitempty"`
|
||||
Interval *Duration `yaml:",omitempty" json:"interval,omitempty"`
|
||||
Retries *uint64 `yaml:",omitempty" json:"retries,omitempty"`
|
||||
StartPeriod *Duration `mapstructure:"start_period" yaml:"start_period,omitempty" json:"start_period,omitempty"`
|
||||
Disable bool `yaml:",omitempty" json:"disable,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// HealthCheckTest is the command run to test the health of a service
|
||||
type HealthCheckTest []string
|
||||
|
||||
// UpdateConfig the service update configuration
|
||||
type UpdateConfig struct {
|
||||
Parallelism *uint64 `yaml:",omitempty" json:"parallelism,omitempty"`
|
||||
Delay Duration `yaml:",omitempty" json:"delay,omitempty"`
|
||||
FailureAction string `mapstructure:"failure_action" yaml:"failure_action,omitempty" json:"failure_action,omitempty"`
|
||||
Monitor Duration `yaml:",omitempty" json:"monitor,omitempty"`
|
||||
MaxFailureRatio float32 `mapstructure:"max_failure_ratio" yaml:"max_failure_ratio,omitempty" json:"max_failure_ratio,omitempty"`
|
||||
Order string `yaml:",omitempty" json:"order,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// Resources the resource limits and reservations
|
||||
type Resources struct {
|
||||
Limits *Resource `yaml:",omitempty" json:"limits,omitempty"`
|
||||
Reservations *Resource `yaml:",omitempty" json:"reservations,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// Resource is a resource to be limited or reserved
|
||||
type Resource struct {
|
||||
// TODO: types to convert from units and ratios
|
||||
NanoCPUs string `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"`
|
||||
MemoryBytes UnitBytes `mapstructure:"memory" yaml:"memory,omitempty" json:"memory,omitempty"`
|
||||
Devices []DeviceRequest `mapstructure:"devices" yaml:"devices,omitempty" json:"devices,omitempty"`
|
||||
GenericResources []GenericResource `mapstructure:"generic_resources" yaml:"generic_resources,omitempty" json:"generic_resources,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
type DeviceRequest struct {
|
||||
Capabilities []string `mapstructure:"capabilities" yaml:"capabilities,omitempty" json:"capabilities,omitempty"`
|
||||
Driver string `mapstructure:"driver" yaml:"driver,omitempty" json:"driver,omitempty"`
|
||||
Count int64 `mapstructure:"count" yaml:"count,omitempty" json:"count,omitempty"`
|
||||
IDs []string `mapstructure:"device_ids" yaml:"device_ids,omitempty" json:"device_ids,omitempty"`
|
||||
}
|
||||
|
||||
// GenericResource represents a "user defined" resource which can
|
||||
// only be an integer (e.g: SSD=3) for a service
|
||||
type GenericResource struct {
|
||||
DiscreteResourceSpec *DiscreteGenericResource `mapstructure:"discrete_resource_spec" yaml:"discrete_resource_spec,omitempty" json:"discrete_resource_spec,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// DiscreteGenericResource represents a "user defined" resource which is defined
|
||||
// as an integer
|
||||
// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...)
|
||||
// Value is used to count the resource (SSD=5, HDD=3, ...)
|
||||
type DiscreteGenericResource struct {
|
||||
Kind string `json:"kind"`
|
||||
Value int64 `json:"value"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// UnitBytes is the bytes type
|
||||
type UnitBytes int64
|
||||
|
||||
// MarshalYAML makes UnitBytes implement yaml.Marshaller
|
||||
func (u UnitBytes) MarshalYAML() (interface{}, error) {
|
||||
return fmt.Sprintf("%d", u), nil
|
||||
}
|
||||
|
||||
// MarshalJSON makes UnitBytes implement json.Marshaler
|
||||
func (u UnitBytes) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`"%d"`, u)), nil
|
||||
}
|
||||
|
||||
// RestartPolicy the service restart policy
|
||||
type RestartPolicy struct {
|
||||
Condition string `yaml:",omitempty" json:"condition,omitempty"`
|
||||
Delay *Duration `yaml:",omitempty" json:"delay,omitempty"`
|
||||
MaxAttempts *uint64 `mapstructure:"max_attempts" yaml:"max_attempts,omitempty" json:"max_attempts,omitempty"`
|
||||
Window *Duration `yaml:",omitempty" json:"window,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// Placement constraints for the service
|
||||
type Placement struct {
|
||||
Constraints []string `yaml:",omitempty" json:"constraints,omitempty"`
|
||||
Preferences []PlacementPreferences `yaml:",omitempty" json:"preferences,omitempty"`
|
||||
MaxReplicas uint64 `mapstructure:"max_replicas_per_node" yaml:"max_replicas_per_node,omitempty" json:"max_replicas_per_node,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// PlacementPreferences is the preferences for a service placement
|
||||
type PlacementPreferences struct {
|
||||
Spread string `yaml:",omitempty" json:"spread,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// ServiceNetworkConfig is the network configuration for a service
|
||||
type ServiceNetworkConfig struct {
|
||||
Priority int `yaml:",omitempty" json:"priotirt,omitempty"`
|
||||
Aliases []string `yaml:",omitempty" json:"aliases,omitempty"`
|
||||
Ipv4Address string `mapstructure:"ipv4_address" yaml:"ipv4_address,omitempty" json:"ipv4_address,omitempty"`
|
||||
Ipv6Address string `mapstructure:"ipv6_address" yaml:"ipv6_address,omitempty" json:"ipv6_address,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// ServicePortConfig is the port configuration for a service
|
||||
type ServicePortConfig struct {
|
||||
Mode string `yaml:",omitempty" json:"mode,omitempty"`
|
||||
HostIP string `mapstructure:"host_ip" yaml:"host_ip,omitempty" json:"host_ip,omitempty"`
|
||||
Target uint32 `yaml:",omitempty" json:"target,omitempty"`
|
||||
Published uint32 `yaml:",omitempty" json:"published,omitempty"`
|
||||
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// ParsePortConfig parse short syntax for service port configuration
|
||||
func ParsePortConfig(value string) ([]ServicePortConfig, error) {
|
||||
var portConfigs []ServicePortConfig
|
||||
ports, portBindings, err := nat.ParsePortSpecs([]string{value})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// We need to sort the key of the ports to make sure it is consistent
|
||||
keys := []string{}
|
||||
for port := range ports {
|
||||
keys = append(keys, string(port))
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
port := nat.Port(key)
|
||||
converted, err := convertPortToPortConfig(port, portBindings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
portConfigs = append(portConfigs, converted...)
|
||||
}
|
||||
return portConfigs, nil
|
||||
}
|
||||
|
||||
func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.PortBinding) ([]ServicePortConfig, error) {
|
||||
portConfigs := []ServicePortConfig{}
|
||||
for _, binding := range portBindings[port] {
|
||||
startHostPort, endHostPort, err := nat.ParsePortRange(binding.HostPort)
|
||||
|
||||
if err != nil && binding.HostPort != "" {
|
||||
return nil, fmt.Errorf("invalid hostport binding (%s) for port (%s)", binding.HostPort, port.Port())
|
||||
}
|
||||
|
||||
for i := startHostPort; i <= endHostPort; i++ {
|
||||
portConfigs = append(portConfigs, ServicePortConfig{
|
||||
HostIP: binding.HostIP,
|
||||
Protocol: strings.ToLower(port.Proto()),
|
||||
Target: uint32(port.Int()),
|
||||
Published: uint32(i),
|
||||
Mode: "ingress",
|
||||
})
|
||||
}
|
||||
}
|
||||
return portConfigs, nil
|
||||
}
|
||||
|
||||
// ServiceVolumeConfig are references to a volume used by a service
|
||||
type ServiceVolumeConfig struct {
|
||||
Type string `yaml:",omitempty" json:"type,omitempty"`
|
||||
Source string `yaml:",omitempty" json:"source,omitempty"`
|
||||
Target string `yaml:",omitempty" json:"target,omitempty"`
|
||||
ReadOnly bool `mapstructure:"read_only" yaml:"read_only,omitempty" json:"read_only,omitempty"`
|
||||
Consistency string `yaml:",omitempty" json:"consistency,omitempty"`
|
||||
Bind *ServiceVolumeBind `yaml:",omitempty" json:"bind,omitempty"`
|
||||
Volume *ServiceVolumeVolume `yaml:",omitempty" json:"volume,omitempty"`
|
||||
Tmpfs *ServiceVolumeTmpfs `yaml:",omitempty" json:"tmpfs,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
const (
|
||||
// TypeBind is the type for mounting host dir
|
||||
VolumeTypeBind = "bind"
|
||||
// TypeVolume is the type for remote storage volumes
|
||||
VolumeTypeVolume = "volume"
|
||||
// TypeTmpfs is the type for mounting tmpfs
|
||||
VolumeTypeTmpfs = "tmpfs"
|
||||
// TypeNamedPipe is the type for mounting Windows named pipes
|
||||
VolumeTypeNamedPipe = "npipe"
|
||||
)
|
||||
|
||||
// ServiceVolumeBind are options for a service volume of type bind
|
||||
type ServiceVolumeBind struct {
|
||||
Propagation string `yaml:",omitempty" json:"propagation,omitempty"`
|
||||
CreateHostPath bool `mapstructure:"create_host_path" yaml:"create_host_path,omitempty" json:"create_host_path,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// Propagation represents the propagation of a mount.
|
||||
const (
|
||||
// PropagationRPrivate RPRIVATE
|
||||
PropagationRPrivate string = "rprivate"
|
||||
// PropagationPrivate PRIVATE
|
||||
PropagationPrivate string = "private"
|
||||
// PropagationRShared RSHARED
|
||||
PropagationRShared string = "rshared"
|
||||
// PropagationShared SHARED
|
||||
PropagationShared string = "shared"
|
||||
// PropagationRSlave RSLAVE
|
||||
PropagationRSlave string = "rslave"
|
||||
// PropagationSlave SLAVE
|
||||
PropagationSlave string = "slave"
|
||||
)
|
||||
|
||||
// ServiceVolumeVolume are options for a service volume of type volume
|
||||
type ServiceVolumeVolume struct {
|
||||
NoCopy bool `mapstructure:"nocopy" yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// ServiceVolumeTmpfs are options for a service volume of type tmpfs
|
||||
type ServiceVolumeTmpfs struct {
|
||||
Size int64 `yaml:",omitempty" json:"size,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// FileReferenceConfig for a reference to a swarm file object
|
||||
type FileReferenceConfig struct {
|
||||
Source string `yaml:",omitempty" json:"source,omitempty"`
|
||||
Target string `yaml:",omitempty" json:"target,omitempty"`
|
||||
UID string `yaml:",omitempty" json:"uid,omitempty"`
|
||||
GID string `yaml:",omitempty" json:"gid,omitempty"`
|
||||
Mode *uint32 `yaml:",omitempty" json:"mode,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// ServiceConfigObjConfig is the config obj configuration for a service
|
||||
type ServiceConfigObjConfig FileReferenceConfig
|
||||
|
||||
// ServiceSecretConfig is the secret configuration for a service
|
||||
type ServiceSecretConfig FileReferenceConfig
|
||||
|
||||
// UlimitsConfig the ulimit configuration
|
||||
type UlimitsConfig struct {
|
||||
Single int `yaml:",omitempty" json:"single,omitempty"`
|
||||
Soft int `yaml:",omitempty" json:"soft,omitempty"`
|
||||
Hard int `yaml:",omitempty" json:"hard,omitempty"`
|
||||
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// MarshalYAML makes UlimitsConfig implement yaml.Marshaller
|
||||
func (u *UlimitsConfig) MarshalYAML() (interface{}, error) {
|
||||
if u.Single != 0 {
|
||||
return u.Single, nil
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// MarshalJSON makes UlimitsConfig implement json.Marshaller
|
||||
func (u *UlimitsConfig) MarshalJSON() ([]byte, error) {
|
||||
if u.Single != 0 {
|
||||
return json.Marshal(u.Single)
|
||||
}
|
||||
// Pass as a value to avoid re-entering this method and use the default implementation
|
||||
return json.Marshal(*u)
|
||||
}
|
||||
|
||||
// NetworkConfig for a network
|
||||
type NetworkConfig struct {
|
||||
Name string `yaml:",omitempty" json:"name,omitempty"`
|
||||
Driver string `yaml:",omitempty" json:"driver,omitempty"`
|
||||
DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"`
|
||||
Ipam IPAMConfig `yaml:",omitempty" json:"ipam,omitempty"`
|
||||
External External `yaml:",omitempty" json:"external,omitempty"`
|
||||
Internal bool `yaml:",omitempty" json:"internal,omitempty"`
|
||||
Attachable bool `yaml:",omitempty" json:"attachable,omitempty"`
|
||||
Labels Labels `yaml:",omitempty" json:"labels,omitempty"`
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// IPAMConfig for a network
|
||||
type IPAMConfig struct {
|
||||
Driver string `yaml:",omitempty" json:"driver,omitempty"`
|
||||
Config []*IPAMPool `yaml:",omitempty" json:"config,omitempty"`
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// IPAMPool for a network
|
||||
type IPAMPool struct {
|
||||
Subnet string `yaml:",omitempty" json:"subnet,omitempty"`
|
||||
Gateway string `yaml:",omitempty" json:"gateway,omitempty"`
|
||||
IPRange string `mapstructure:"ip_range" yaml:"ip_range,omitempty" json:"ip_range,omitempty"`
|
||||
AuxiliaryAddresses map[string]string `mapstructure:"aux_addresses" yaml:"aux_addresses,omitempty" json:"aux_addresses,omitempty"`
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// VolumeConfig for a volume
|
||||
type VolumeConfig struct {
|
||||
Name string `yaml:",omitempty" json:"name,omitempty"`
|
||||
Driver string `yaml:",omitempty" json:"driver,omitempty"`
|
||||
DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"`
|
||||
External External `yaml:",omitempty" json:"external,omitempty"`
|
||||
Labels Labels `yaml:",omitempty" json:"labels,omitempty"`
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// External identifies a Volume or Network as a reference to a resource that is
|
||||
// not managed, and should already exist.
|
||||
// External.name is deprecated and replaced by Volume.name
|
||||
type External struct {
|
||||
Name string `yaml:",omitempty" json:"name,omitempty"`
|
||||
External bool `yaml:",omitempty" json:"external,omitempty"`
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// MarshalYAML makes External implement yaml.Marshaller
|
||||
func (e External) MarshalYAML() (interface{}, error) {
|
||||
if e.Name == "" {
|
||||
return e.External, nil
|
||||
}
|
||||
return External{Name: e.Name}, nil
|
||||
}
|
||||
|
||||
// MarshalJSON makes External implement json.Marshaller
|
||||
func (e External) MarshalJSON() ([]byte, error) {
|
||||
if e.Name == "" {
|
||||
return []byte(fmt.Sprintf("%v", e.External)), nil
|
||||
}
|
||||
return []byte(fmt.Sprintf(`{"name": %q}`, e.Name)), nil
|
||||
}
|
||||
|
||||
// CredentialSpecConfig for credential spec on Windows
|
||||
type CredentialSpecConfig struct {
|
||||
Config string `yaml:",omitempty" json:"config,omitempty"` // Config was added in API v1.40
|
||||
File string `yaml:",omitempty" json:"file,omitempty"`
|
||||
Registry string `yaml:",omitempty" json:"registry,omitempty"`
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// FileObjectConfig is a config type for a file used by a service
|
||||
type FileObjectConfig struct {
|
||||
Name string `yaml:",omitempty" json:"name,omitempty"`
|
||||
File string `yaml:",omitempty" json:"file,omitempty"`
|
||||
External External `yaml:",omitempty" json:"external,omitempty"`
|
||||
Labels Labels `yaml:",omitempty" json:"labels,omitempty"`
|
||||
Driver string `yaml:",omitempty" json:"driver,omitempty"`
|
||||
DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"`
|
||||
TemplateDriver string `mapstructure:"template_driver" yaml:"template_driver,omitempty" json:"template_driver,omitempty"`
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
const (
|
||||
// ServiceConditionCompletedSuccessfully is the type for waiting until a service has completed successfully (exit code 0).
|
||||
ServiceConditionCompletedSuccessfully = "service_completed_successfully"
|
||||
|
||||
// ServiceConditionHealthy is the type for waiting until a service is healthy.
|
||||
ServiceConditionHealthy = "service_healthy"
|
||||
|
||||
// ServiceConditionStarted is the type for waiting until a service has started (default).
|
||||
ServiceConditionStarted = "service_started"
|
||||
)
|
||||
|
||||
type DependsOnConfig map[string]ServiceDependency
|
||||
|
||||
type ServiceDependency struct {
|
||||
Condition string `yaml:",omitempty" json:"condition,omitempty"`
|
||||
Extensions map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
type ExtendsConfig MappingWithEquals
|
||||
|
||||
// SecretConfig for a secret
|
||||
type SecretConfig FileObjectConfig
|
||||
|
||||
// ConfigObjConfig is the config for the swarm "Config" object
|
||||
type ConfigObjConfig FileObjectConfig
|
274
pkg/third_party/compose-go/types/types_test.go
vendored
Executable file
274
pkg/third_party/compose-go/types/types_test.go
vendored
Executable file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestParsePortConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value string
|
||||
expectedError string
|
||||
expected []ServicePortConfig
|
||||
}{
|
||||
{
|
||||
value: "80",
|
||||
expected: []ServicePortConfig{
|
||||
{
|
||||
Protocol: "tcp",
|
||||
Target: 80,
|
||||
Mode: "ingress",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: "80:8080",
|
||||
expected: []ServicePortConfig{
|
||||
{
|
||||
Protocol: "tcp",
|
||||
Target: 8080,
|
||||
Published: 80,
|
||||
Mode: "ingress",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: "8080:80/tcp",
|
||||
expected: []ServicePortConfig{
|
||||
{
|
||||
Protocol: "tcp",
|
||||
Target: 80,
|
||||
Published: 8080,
|
||||
Mode: "ingress",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: "80:8080/udp",
|
||||
expected: []ServicePortConfig{
|
||||
{
|
||||
Protocol: "udp",
|
||||
Target: 8080,
|
||||
Published: 80,
|
||||
Mode: "ingress",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: "80-81:8080-8081/tcp",
|
||||
expected: []ServicePortConfig{
|
||||
{
|
||||
Protocol: "tcp",
|
||||
Target: 8080,
|
||||
Published: 80,
|
||||
Mode: "ingress",
|
||||
},
|
||||
{
|
||||
Protocol: "tcp",
|
||||
Target: 8081,
|
||||
Published: 81,
|
||||
Mode: "ingress",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: "80-82:8080-8082/udp",
|
||||
expected: []ServicePortConfig{
|
||||
{
|
||||
Protocol: "udp",
|
||||
Target: 8080,
|
||||
Published: 80,
|
||||
Mode: "ingress",
|
||||
},
|
||||
{
|
||||
Protocol: "udp",
|
||||
Target: 8081,
|
||||
Published: 81,
|
||||
Mode: "ingress",
|
||||
},
|
||||
{
|
||||
Protocol: "udp",
|
||||
Target: 8082,
|
||||
Published: 82,
|
||||
Mode: "ingress",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: "80-82:8080/udp",
|
||||
expected: []ServicePortConfig{
|
||||
{
|
||||
Protocol: "udp",
|
||||
Target: 8080,
|
||||
Published: 80,
|
||||
Mode: "ingress",
|
||||
},
|
||||
{
|
||||
Protocol: "udp",
|
||||
Target: 8080,
|
||||
Published: 81,
|
||||
Mode: "ingress",
|
||||
},
|
||||
{
|
||||
Protocol: "udp",
|
||||
Target: 8080,
|
||||
Published: 82,
|
||||
Mode: "ingress",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: "80-80:8080/tcp",
|
||||
expected: []ServicePortConfig{
|
||||
{
|
||||
Protocol: "tcp",
|
||||
Target: 8080,
|
||||
Published: 80,
|
||||
Mode: "ingress",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: "9999999",
|
||||
expectedError: "Invalid containerPort: 9999999",
|
||||
},
|
||||
{
|
||||
value: "80/xyz",
|
||||
expectedError: "Invalid proto: xyz",
|
||||
},
|
||||
{
|
||||
value: "tcp",
|
||||
expectedError: "Invalid containerPort: tcp",
|
||||
},
|
||||
{
|
||||
value: "udp",
|
||||
expectedError: "Invalid containerPort: udp",
|
||||
},
|
||||
{
|
||||
value: "",
|
||||
expectedError: "No port specified: <empty>",
|
||||
},
|
||||
{
|
||||
value: "1.1.1.1:80:80",
|
||||
expected: []ServicePortConfig{
|
||||
{
|
||||
HostIP: "1.1.1.1",
|
||||
Protocol: "tcp",
|
||||
Target: 80,
|
||||
Published: 80,
|
||||
Mode: "ingress",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
ports, err := ParsePortConfig(tc.value)
|
||||
if tc.expectedError != "" {
|
||||
assert.Error(t, err, tc.expectedError)
|
||||
continue
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Len(ports, len(tc.expected)))
|
||||
for _, expectedPortConfig := range tc.expected {
|
||||
assertContains(t, ports, expectedPortConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertContains(t *testing.T, portConfigs []ServicePortConfig, expected ServicePortConfig) {
|
||||
var contains = false
|
||||
for _, portConfig := range portConfigs {
|
||||
if is.DeepEqual(portConfig, expected)().Success() {
|
||||
contains = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !contains {
|
||||
t.Errorf("expected %v to contain %v, did not", portConfigs, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
s := make(set)
|
||||
s.append("one")
|
||||
s.append("two")
|
||||
s.append("three")
|
||||
s.append("two")
|
||||
assert.Equal(t, len(s.toSlice()), 3)
|
||||
}
|
||||
|
||||
type foo struct {
|
||||
Bar string
|
||||
}
|
||||
|
||||
func TestExtension(t *testing.T) {
|
||||
x := Extensions{
|
||||
"foo": map[string]interface{}{
|
||||
"bar": "zot",
|
||||
},
|
||||
}
|
||||
var foo foo
|
||||
ok, err := x.Get("foo", &foo)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, ok == true)
|
||||
assert.Check(t, foo.Bar == "zot")
|
||||
|
||||
ok, err = x.Get("qiz", &foo)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, ok == false)
|
||||
}
|
||||
|
||||
func TestNewMapping(t *testing.T) {
|
||||
m := NewMapping([]string{
|
||||
"FOO=BAR",
|
||||
"ZOT=",
|
||||
"QIX",
|
||||
})
|
||||
mw := NewMappingWithEquals([]string{
|
||||
"FOO=BAR",
|
||||
"ZOT=",
|
||||
"QIX",
|
||||
})
|
||||
assert.Check(t, m["FOO"] == "BAR")
|
||||
assert.Check(t, m["ZOT"] == "")
|
||||
assert.Check(t, m["QIX"] == "")
|
||||
assert.Check(t, *mw["FOO"] == "BAR")
|
||||
assert.Check(t, *mw["ZOT"] == "")
|
||||
assert.Check(t, mw["QIX"] == nil)
|
||||
}
|
||||
|
||||
func TestNetworksByPriority(t *testing.T) {
|
||||
s := ServiceConfig{
|
||||
Networks: map[string]*ServiceNetworkConfig{
|
||||
"foo": nil,
|
||||
"bar": {
|
||||
Priority: 10,
|
||||
},
|
||||
"zot": {
|
||||
Priority: 100,
|
||||
},
|
||||
"qix": {
|
||||
Priority: 1000,
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.DeepEqual(t, s.NetworksByPriority(), []string{"qix", "zot", "bar", "foo"})
|
||||
}
|
1
vendor/github.com/Microsoft/go-winio/CODEOWNERS
generated
vendored
Normal file
1
vendor/github.com/Microsoft/go-winio/CODEOWNERS
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
* @microsoft/containerplat
|
2
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
2
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
@ -1,4 +1,4 @@
|
||||
# go-winio
|
||||
# go-winio [](https://github.com/microsoft/go-winio/actions/workflows/ci.yml)
|
||||
|
||||
This repository contains utilities for efficiently performing Win32 IO operations in
|
||||
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
||||
|
36
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
36
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
@ -5,21 +5,14 @@ package winio
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
|
||||
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
|
||||
|
||||
const (
|
||||
fileBasicInfo = 0
|
||||
fileIDInfo = 0x12
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// FileBasicInfo contains file access time and file attributes information.
|
||||
type FileBasicInfo struct {
|
||||
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime
|
||||
CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime
|
||||
FileAttributes uint32
|
||||
pad uint32 // padding
|
||||
}
|
||||
@ -27,7 +20,7 @@ type FileBasicInfo struct {
|
||||
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||
bi := &FileBasicInfo{}
|
||||
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(f)
|
||||
@ -36,13 +29,32 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||
|
||||
// SetFileBasicInfo sets times and attributes for a file.
|
||||
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||
if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FileStandardInfo contains extended information for the file.
|
||||
// FILE_STANDARD_INFO in WinBase.h
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
|
||||
type FileStandardInfo struct {
|
||||
AllocationSize, EndOfFile int64
|
||||
NumberOfLinks uint32
|
||||
DeletePending, Directory bool
|
||||
}
|
||||
|
||||
// GetFileStandardInfo retrieves ended information for the file.
|
||||
func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) {
|
||||
si := &FileStandardInfo{}
|
||||
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil {
|
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(f)
|
||||
return si, nil
|
||||
}
|
||||
|
||||
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||
// unique on a system.
|
||||
type FileIDInfo struct {
|
||||
@ -53,7 +65,7 @@ type FileIDInfo struct {
|
||||
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
||||
func GetFileID(f *os.File) (*FileIDInfo, error) {
|
||||
fileID := &FileIDInfo{}
|
||||
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
|
||||
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
|
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(f)
|
||||
|
6
vendor/github.com/Microsoft/go-winio/go.mod
generated
vendored
6
vendor/github.com/Microsoft/go-winio/go.mod
generated
vendored
@ -3,7 +3,7 @@ module github.com/Microsoft/go-winio
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/sirupsen/logrus v1.4.1
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
|
||||
)
|
||||
|
20
vendor/github.com/Microsoft/go-winio/go.sum
generated
vendored
20
vendor/github.com/Microsoft/go-winio/go.sum
generated
vendored
@ -1,18 +1,14 @@
|
||||
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/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
2
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
2
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
@ -1,3 +1,5 @@
|
||||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
|
4
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
4
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
@ -429,10 +429,10 @@ type PipeConfig struct {
|
||||
// when the pipe is in message mode.
|
||||
MessageMode bool
|
||||
|
||||
// InputBufferSize specifies the size the input buffer, in bytes.
|
||||
// InputBufferSize specifies the size of the input buffer, in bytes.
|
||||
InputBufferSize int32
|
||||
|
||||
// OutputBufferSize specifies the size the input buffer, in bytes.
|
||||
// OutputBufferSize specifies the size of the output buffer, in bytes.
|
||||
OutputBufferSize int32
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
generated
vendored
2
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
generated
vendored
@ -1,3 +1,5 @@
|
||||
// +build windows
|
||||
|
||||
// Package guid provides a GUID type. The backing structure for a GUID is
|
||||
// identical to that used by the golang.org/x/sys/windows GUID type.
|
||||
// There are two main binary encodings used for a GUID, the big-endian encoding,
|
||||
|
161
vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go
generated
vendored
Normal file
161
vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
// +build windows
|
||||
|
||||
package security
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type (
|
||||
accessMask uint32
|
||||
accessMode uint32
|
||||
desiredAccess uint32
|
||||
inheritMode uint32
|
||||
objectType uint32
|
||||
shareMode uint32
|
||||
securityInformation uint32
|
||||
trusteeForm uint32
|
||||
trusteeType uint32
|
||||
|
||||
explicitAccess struct {
|
||||
accessPermissions accessMask
|
||||
accessMode accessMode
|
||||
inheritance inheritMode
|
||||
trustee trustee
|
||||
}
|
||||
|
||||
trustee struct {
|
||||
multipleTrustee *trustee
|
||||
multipleTrusteeOperation int32
|
||||
trusteeForm trusteeForm
|
||||
trusteeType trusteeType
|
||||
name uintptr
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
accessMaskDesiredPermission accessMask = 1 << 31 // GENERIC_READ
|
||||
|
||||
accessModeGrant accessMode = 1
|
||||
|
||||
desiredAccessReadControl desiredAccess = 0x20000
|
||||
desiredAccessWriteDac desiredAccess = 0x40000
|
||||
|
||||
gvmga = "GrantVmGroupAccess:"
|
||||
|
||||
inheritModeNoInheritance inheritMode = 0x0
|
||||
inheritModeSubContainersAndObjectsInherit inheritMode = 0x3
|
||||
|
||||
objectTypeFileObject objectType = 0x1
|
||||
|
||||
securityInformationDACL securityInformation = 0x4
|
||||
|
||||
shareModeRead shareMode = 0x1
|
||||
shareModeWrite shareMode = 0x2
|
||||
|
||||
sidVmGroup = "S-1-5-83-0"
|
||||
|
||||
trusteeFormIsSid trusteeForm = 0
|
||||
|
||||
trusteeTypeWellKnownGroup trusteeType = 5
|
||||
)
|
||||
|
||||
// GrantVMGroupAccess sets the DACL for a specified file or directory to
|
||||
// include Grant ACE entries for the VM Group SID. This is a golang re-
|
||||
// implementation of the same function in vmcompute, just not exported in
|
||||
// RS5. Which kind of sucks. Sucks a lot :/
|
||||
func GrantVmGroupAccess(name string) error {
|
||||
// Stat (to determine if `name` is a directory).
|
||||
s, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "%s os.Stat %s", gvmga, name)
|
||||
}
|
||||
|
||||
// Get a handle to the file/directory. Must defer Close on success.
|
||||
fd, err := createFile(name, s.IsDir())
|
||||
if err != nil {
|
||||
return err // Already wrapped
|
||||
}
|
||||
defer syscall.CloseHandle(fd)
|
||||
|
||||
// Get the current DACL and Security Descriptor. Must defer LocalFree on success.
|
||||
ot := objectTypeFileObject
|
||||
si := securityInformationDACL
|
||||
sd := uintptr(0)
|
||||
origDACL := uintptr(0)
|
||||
if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil {
|
||||
return errors.Wrapf(err, "%s GetSecurityInfo %s", gvmga, name)
|
||||
}
|
||||
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd)))
|
||||
|
||||
// Generate a new DACL which is the current DACL with the required ACEs added.
|
||||
// Must defer LocalFree on success.
|
||||
newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), origDACL)
|
||||
if err != nil {
|
||||
return err // Already wrapped
|
||||
}
|
||||
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL)))
|
||||
|
||||
// And finally use SetSecurityInfo to apply the updated DACL.
|
||||
if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil {
|
||||
return errors.Wrapf(err, "%s SetSecurityInfo %s", gvmga, name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createFile is a helper function to call [Nt]CreateFile to get a handle to
|
||||
// the file or directory.
|
||||
func createFile(name string, isDir bool) (syscall.Handle, error) {
|
||||
namep := syscall.StringToUTF16(name)
|
||||
da := uint32(desiredAccessReadControl | desiredAccessWriteDac)
|
||||
sm := uint32(shareModeRead | shareModeWrite)
|
||||
fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL)
|
||||
if isDir {
|
||||
fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
||||
}
|
||||
fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "%s syscall.CreateFile %s", gvmga, name)
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
// generateDACLWithAcesAdded generates a new DACL with the two needed ACEs added.
|
||||
// The caller is responsible for LocalFree of the returned DACL on success.
|
||||
func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) {
|
||||
// Generate pointers to the SIDs based on the string SIDs
|
||||
sid, err := syscall.StringToSid(sidVmGroup)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "%s syscall.StringToSid %s %s", gvmga, name, sidVmGroup)
|
||||
}
|
||||
|
||||
inheritance := inheritModeNoInheritance
|
||||
if isDir {
|
||||
inheritance = inheritModeSubContainersAndObjectsInherit
|
||||
}
|
||||
|
||||
eaArray := []explicitAccess{
|
||||
explicitAccess{
|
||||
accessPermissions: accessMaskDesiredPermission,
|
||||
accessMode: accessModeGrant,
|
||||
inheritance: inheritance,
|
||||
trustee: trustee{
|
||||
trusteeForm: trusteeFormIsSid,
|
||||
trusteeType: trusteeTypeWellKnownGroup,
|
||||
name: uintptr(unsafe.Pointer(sid)),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
modifiedDACL := uintptr(0)
|
||||
if err := setEntriesInAcl(uintptr(uint32(1)), uintptr(unsafe.Pointer(&eaArray[0])), origDACL, &modifiedDACL); err != nil {
|
||||
return 0, errors.Wrapf(err, "%s SetEntriesInAcl %s", gvmga, name)
|
||||
}
|
||||
|
||||
return modifiedDACL, nil
|
||||
}
|
7
vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go
generated
vendored
Normal file
7
vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
package security
|
||||
|
||||
//go:generate go run mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
|
||||
|
||||
//sys getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (err error) [failretval!=0] = advapi32.GetSecurityInfo
|
||||
//sys setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (err error) [failretval!=0] = advapi32.SetSecurityInfo
|
||||
//sys setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (err error) [failretval!=0] = advapi32.SetEntriesInAclW
|
70
vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go
generated
vendored
Normal file
70
vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package security
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
|
||||
procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo")
|
||||
procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW")
|
||||
procSetSecurityInfo = modadvapi32.NewProc("SetSecurityInfo")
|
||||
)
|
||||
|
||||
func getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(ppsidOwner)), uintptr(unsafe.Pointer(ppsidGroup)), uintptr(unsafe.Pointer(ppDacl)), uintptr(unsafe.Pointer(ppSacl)), uintptr(unsafe.Pointer(ppSecurityDescriptor)), 0)
|
||||
if r1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetEntriesInAclW.Addr(), 4, uintptr(count), uintptr(pListOfEEs), uintptr(oldAcl), uintptr(unsafe.Pointer(newAcl)), 0, 0)
|
||||
if r1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procSetSecurityInfo.Addr(), 7, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(psidOwner), uintptr(psidGroup), uintptr(pDacl), uintptr(pSacl), 0, 0)
|
||||
if r1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
5
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
5
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
@ -28,8 +28,9 @@ const (
|
||||
|
||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
|
||||
|
||||
SeBackupPrivilege = "SeBackupPrivilege"
|
||||
SeRestorePrivilege = "SeRestorePrivilege"
|
||||
SeBackupPrivilege = "SeBackupPrivilege"
|
||||
SeRestorePrivilege = "SeRestorePrivilege"
|
||||
SeSecurityPrivilege = "SeSecurityPrivilege"
|
||||
)
|
||||
|
||||
const (
|
||||
|
2
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
2
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
@ -1,3 +1,3 @@
|
||||
package winio
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go
|
||||
|
323
vendor/github.com/Microsoft/go-winio/vhd/vhd.go
generated
vendored
Normal file
323
vendor/github.com/Microsoft/go-winio/vhd/vhd.go
generated
vendored
Normal file
@ -0,0 +1,323 @@
|
||||
// +build windows
|
||||
|
||||
package vhd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/Microsoft/go-winio/pkg/guid"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//go:generate go run mksyscall_windows.go -output zvhd_windows.go vhd.go
|
||||
|
||||
//sys createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (err error) [failretval != 0] = virtdisk.CreateVirtualDisk
|
||||
//sys openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (err error) [failretval != 0] = virtdisk.OpenVirtualDisk
|
||||
//sys attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (err error) [failretval != 0] = virtdisk.AttachVirtualDisk
|
||||
//sys detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (err error) [failretval != 0] = virtdisk.DetachVirtualDisk
|
||||
//sys getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (err error) [failretval != 0] = virtdisk.GetVirtualDiskPhysicalPath
|
||||
|
||||
type (
|
||||
CreateVirtualDiskFlag uint32
|
||||
VirtualDiskFlag uint32
|
||||
AttachVirtualDiskFlag uint32
|
||||
DetachVirtualDiskFlag uint32
|
||||
VirtualDiskAccessMask uint32
|
||||
)
|
||||
|
||||
type VirtualStorageType struct {
|
||||
DeviceID uint32
|
||||
VendorID guid.GUID
|
||||
}
|
||||
|
||||
type CreateVersion2 struct {
|
||||
UniqueID guid.GUID
|
||||
MaximumSize uint64
|
||||
BlockSizeInBytes uint32
|
||||
SectorSizeInBytes uint32
|
||||
PhysicalSectorSizeInByte uint32
|
||||
ParentPath *uint16 // string
|
||||
SourcePath *uint16 // string
|
||||
OpenFlags uint32
|
||||
ParentVirtualStorageType VirtualStorageType
|
||||
SourceVirtualStorageType VirtualStorageType
|
||||
ResiliencyGUID guid.GUID
|
||||
}
|
||||
|
||||
type CreateVirtualDiskParameters struct {
|
||||
Version uint32 // Must always be set to 2
|
||||
Version2 CreateVersion2
|
||||
}
|
||||
|
||||
type OpenVersion2 struct {
|
||||
GetInfoOnly bool
|
||||
ReadOnly bool
|
||||
ResiliencyGUID guid.GUID
|
||||
}
|
||||
|
||||
type OpenVirtualDiskParameters struct {
|
||||
Version uint32 // Must always be set to 2
|
||||
Version2 OpenVersion2
|
||||
}
|
||||
|
||||
type AttachVersion2 struct {
|
||||
RestrictedOffset uint64
|
||||
RestrictedLength uint64
|
||||
}
|
||||
|
||||
type AttachVirtualDiskParameters struct {
|
||||
Version uint32 // Must always be set to 2
|
||||
Version2 AttachVersion2
|
||||
}
|
||||
|
||||
const (
|
||||
VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3
|
||||
|
||||
// Access Mask for opening a VHD
|
||||
VirtualDiskAccessNone VirtualDiskAccessMask = 0x00000000
|
||||
VirtualDiskAccessAttachRO VirtualDiskAccessMask = 0x00010000
|
||||
VirtualDiskAccessAttachRW VirtualDiskAccessMask = 0x00020000
|
||||
VirtualDiskAccessDetach VirtualDiskAccessMask = 0x00040000
|
||||
VirtualDiskAccessGetInfo VirtualDiskAccessMask = 0x00080000
|
||||
VirtualDiskAccessCreate VirtualDiskAccessMask = 0x00100000
|
||||
VirtualDiskAccessMetaOps VirtualDiskAccessMask = 0x00200000
|
||||
VirtualDiskAccessRead VirtualDiskAccessMask = 0x000d0000
|
||||
VirtualDiskAccessAll VirtualDiskAccessMask = 0x003f0000
|
||||
VirtualDiskAccessWritable VirtualDiskAccessMask = 0x00320000
|
||||
|
||||
// Flags for creating a VHD
|
||||
CreateVirtualDiskFlagNone CreateVirtualDiskFlag = 0x0
|
||||
CreateVirtualDiskFlagFullPhysicalAllocation CreateVirtualDiskFlag = 0x1
|
||||
CreateVirtualDiskFlagPreventWritesToSourceDisk CreateVirtualDiskFlag = 0x2
|
||||
CreateVirtualDiskFlagDoNotCopyMetadataFromParent CreateVirtualDiskFlag = 0x4
|
||||
CreateVirtualDiskFlagCreateBackingStorage CreateVirtualDiskFlag = 0x8
|
||||
CreateVirtualDiskFlagUseChangeTrackingSourceLimit CreateVirtualDiskFlag = 0x10
|
||||
CreateVirtualDiskFlagPreserveParentChangeTrackingState CreateVirtualDiskFlag = 0x20
|
||||
CreateVirtualDiskFlagVhdSetUseOriginalBackingStorage CreateVirtualDiskFlag = 0x40
|
||||
CreateVirtualDiskFlagSparseFile CreateVirtualDiskFlag = 0x80
|
||||
CreateVirtualDiskFlagPmemCompatible CreateVirtualDiskFlag = 0x100
|
||||
CreateVirtualDiskFlagSupportCompressedVolumes CreateVirtualDiskFlag = 0x200
|
||||
|
||||
// Flags for opening a VHD
|
||||
OpenVirtualDiskFlagNone VirtualDiskFlag = 0x00000000
|
||||
OpenVirtualDiskFlagNoParents VirtualDiskFlag = 0x00000001
|
||||
OpenVirtualDiskFlagBlankFile VirtualDiskFlag = 0x00000002
|
||||
OpenVirtualDiskFlagBootDrive VirtualDiskFlag = 0x00000004
|
||||
OpenVirtualDiskFlagCachedIO VirtualDiskFlag = 0x00000008
|
||||
OpenVirtualDiskFlagCustomDiffChain VirtualDiskFlag = 0x00000010
|
||||
OpenVirtualDiskFlagParentCachedIO VirtualDiskFlag = 0x00000020
|
||||
OpenVirtualDiskFlagVhdsetFileOnly VirtualDiskFlag = 0x00000040
|
||||
OpenVirtualDiskFlagIgnoreRelativeParentLocator VirtualDiskFlag = 0x00000080
|
||||
OpenVirtualDiskFlagNoWriteHardening VirtualDiskFlag = 0x00000100
|
||||
OpenVirtualDiskFlagSupportCompressedVolumes VirtualDiskFlag = 0x00000200
|
||||
|
||||
// Flags for attaching a VHD
|
||||
AttachVirtualDiskFlagNone AttachVirtualDiskFlag = 0x00000000
|
||||
AttachVirtualDiskFlagReadOnly AttachVirtualDiskFlag = 0x00000001
|
||||
AttachVirtualDiskFlagNoDriveLetter AttachVirtualDiskFlag = 0x00000002
|
||||
AttachVirtualDiskFlagPermanentLifetime AttachVirtualDiskFlag = 0x00000004
|
||||
AttachVirtualDiskFlagNoLocalHost AttachVirtualDiskFlag = 0x00000008
|
||||
AttachVirtualDiskFlagNoSecurityDescriptor AttachVirtualDiskFlag = 0x00000010
|
||||
AttachVirtualDiskFlagBypassDefaultEncryptionPolicy AttachVirtualDiskFlag = 0x00000020
|
||||
AttachVirtualDiskFlagNonPnp AttachVirtualDiskFlag = 0x00000040
|
||||
AttachVirtualDiskFlagRestrictedRange AttachVirtualDiskFlag = 0x00000080
|
||||
AttachVirtualDiskFlagSinglePartition AttachVirtualDiskFlag = 0x00000100
|
||||
AttachVirtualDiskFlagRegisterVolume AttachVirtualDiskFlag = 0x00000200
|
||||
|
||||
// Flags for detaching a VHD
|
||||
DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0
|
||||
)
|
||||
|
||||
// CreateVhdx is a helper function to create a simple vhdx file at the given path using
|
||||
// default values.
|
||||
func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error {
|
||||
params := CreateVirtualDiskParameters{
|
||||
Version: 2,
|
||||
Version2: CreateVersion2{
|
||||
MaximumSize: uint64(maxSizeInGb) * 1024 * 1024 * 1024,
|
||||
BlockSizeInBytes: blockSizeInMb * 1024 * 1024,
|
||||
},
|
||||
}
|
||||
|
||||
handle, err := CreateVirtualDisk(path, VirtualDiskAccessNone, CreateVirtualDiskFlagNone, ¶ms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := syscall.CloseHandle(handle); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DetachVirtualDisk detaches a virtual hard disk by handle.
|
||||
func DetachVirtualDisk(handle syscall.Handle) (err error) {
|
||||
if err := detachVirtualDisk(handle, 0, 0); err != nil {
|
||||
return errors.Wrap(err, "failed to detach virtual disk")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DetachVhd detaches a vhd found at `path`.
|
||||
func DetachVhd(path string) error {
|
||||
handle, err := OpenVirtualDisk(
|
||||
path,
|
||||
VirtualDiskAccessNone,
|
||||
OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
return DetachVirtualDisk(handle)
|
||||
}
|
||||
|
||||
// AttachVirtualDisk attaches a virtual hard disk for use.
|
||||
func AttachVirtualDisk(handle syscall.Handle, attachVirtualDiskFlag AttachVirtualDiskFlag, parameters *AttachVirtualDiskParameters) (err error) {
|
||||
// Supports both version 1 and 2 of the attach parameters as version 2 wasn't present in RS5.
|
||||
if err := attachVirtualDisk(
|
||||
handle,
|
||||
nil,
|
||||
uint32(attachVirtualDiskFlag),
|
||||
0,
|
||||
parameters,
|
||||
nil,
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "failed to attach virtual disk")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AttachVhd attaches a virtual hard disk at `path` for use. Attaches using version 2
|
||||
// of the ATTACH_VIRTUAL_DISK_PARAMETERS.
|
||||
func AttachVhd(path string) (err error) {
|
||||
handle, err := OpenVirtualDisk(
|
||||
path,
|
||||
VirtualDiskAccessNone,
|
||||
OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer syscall.CloseHandle(handle)
|
||||
params := AttachVirtualDiskParameters{Version: 2}
|
||||
if err := AttachVirtualDisk(
|
||||
handle,
|
||||
AttachVirtualDiskFlagNone,
|
||||
¶ms,
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "failed to attach virtual disk")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags.
|
||||
func OpenVirtualDisk(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag) (syscall.Handle, error) {
|
||||
parameters := OpenVirtualDiskParameters{Version: 2}
|
||||
handle, err := OpenVirtualDiskWithParameters(
|
||||
vhdPath,
|
||||
virtualDiskAccessMask,
|
||||
openVirtualDiskFlags,
|
||||
¶meters,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
// OpenVirtualDiskWithParameters obtains a handle to a VHD opened with supplied access mask, flags and parameters.
|
||||
func OpenVirtualDiskWithParameters(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag, parameters *OpenVirtualDiskParameters) (syscall.Handle, error) {
|
||||
var (
|
||||
handle syscall.Handle
|
||||
defaultType VirtualStorageType
|
||||
)
|
||||
if parameters.Version != 2 {
|
||||
return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version)
|
||||
}
|
||||
if err := openVirtualDisk(
|
||||
&defaultType,
|
||||
vhdPath,
|
||||
uint32(virtualDiskAccessMask),
|
||||
uint32(openVirtualDiskFlags),
|
||||
parameters,
|
||||
&handle,
|
||||
); err != nil {
|
||||
return 0, errors.Wrap(err, "failed to open virtual disk")
|
||||
}
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
// CreateVirtualDisk creates a virtual harddisk and returns a handle to the disk.
|
||||
func CreateVirtualDisk(path string, virtualDiskAccessMask VirtualDiskAccessMask, createVirtualDiskFlags CreateVirtualDiskFlag, parameters *CreateVirtualDiskParameters) (syscall.Handle, error) {
|
||||
var (
|
||||
handle syscall.Handle
|
||||
defaultType VirtualStorageType
|
||||
)
|
||||
if parameters.Version != 2 {
|
||||
return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version)
|
||||
}
|
||||
|
||||
if err := createVirtualDisk(
|
||||
&defaultType,
|
||||
path,
|
||||
uint32(virtualDiskAccessMask),
|
||||
nil,
|
||||
uint32(createVirtualDiskFlags),
|
||||
0,
|
||||
parameters,
|
||||
nil,
|
||||
&handle,
|
||||
); err != nil {
|
||||
return handle, errors.Wrap(err, "failed to create virtual disk")
|
||||
}
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
// GetVirtualDiskPhysicalPath takes a handle to a virtual hard disk and returns the physical
|
||||
// path of the disk on the machine. This path is in the form \\.\PhysicalDriveX where X is an integer
|
||||
// that represents the particular enumeration of the physical disk on the caller's system.
|
||||
func GetVirtualDiskPhysicalPath(handle syscall.Handle) (_ string, err error) {
|
||||
var (
|
||||
diskPathSizeInBytes uint32 = 256 * 2 // max path length 256 wide chars
|
||||
diskPhysicalPathBuf [256]uint16
|
||||
)
|
||||
if err := getVirtualDiskPhysicalPath(
|
||||
handle,
|
||||
&diskPathSizeInBytes,
|
||||
&diskPhysicalPathBuf[0],
|
||||
); err != nil {
|
||||
return "", errors.Wrap(err, "failed to get disk physical path")
|
||||
}
|
||||
return windows.UTF16ToString(diskPhysicalPathBuf[:]), nil
|
||||
}
|
||||
|
||||
// CreateDiffVhd is a helper function to create a differencing virtual disk.
|
||||
func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error {
|
||||
// Setting `ParentPath` is how to signal to create a differencing disk.
|
||||
createParams := &CreateVirtualDiskParameters{
|
||||
Version: 2,
|
||||
Version2: CreateVersion2{
|
||||
ParentPath: windows.StringToUTF16Ptr(baseVhdPath),
|
||||
BlockSizeInBytes: blockSizeInMB * 1024 * 1024,
|
||||
OpenFlags: uint32(OpenVirtualDiskFlagCachedIO),
|
||||
},
|
||||
}
|
||||
|
||||
vhdHandle, err := CreateVirtualDisk(
|
||||
diffVhdPath,
|
||||
VirtualDiskAccessNone,
|
||||
CreateVirtualDiskFlagNone,
|
||||
createParams,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create differencing vhd: %s", err)
|
||||
}
|
||||
if err := syscall.CloseHandle(vhdHandle); err != nil {
|
||||
return fmt.Errorf("failed to close differencing vhd handle: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
106
vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go
generated
vendored
Normal file
106
vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package vhd
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modvirtdisk = windows.NewLazySystemDLL("virtdisk.dll")
|
||||
|
||||
procAttachVirtualDisk = modvirtdisk.NewProc("AttachVirtualDisk")
|
||||
procCreateVirtualDisk = modvirtdisk.NewProc("CreateVirtualDisk")
|
||||
procDetachVirtualDisk = modvirtdisk.NewProc("DetachVirtualDisk")
|
||||
procGetVirtualDiskPhysicalPath = modvirtdisk.NewProc("GetVirtualDiskPhysicalPath")
|
||||
procOpenVirtualDisk = modvirtdisk.NewProc("OpenVirtualDisk")
|
||||
)
|
||||
|
||||
func attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procAttachVirtualDisk.Addr(), 6, uintptr(handle), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(attachVirtualDiskFlag), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)))
|
||||
if r1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _createVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, securityDescriptor, createVirtualDiskFlags, providerSpecificFlags, parameters, overlapped, handle)
|
||||
}
|
||||
|
||||
func _createVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procCreateVirtualDisk.Addr(), 9, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(createVirtualDiskFlags), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(handle)))
|
||||
if r1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDetachVirtualDisk.Addr(), 3, uintptr(handle), uintptr(detachVirtualDiskFlags), uintptr(providerSpecificFlags))
|
||||
if r1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetVirtualDiskPhysicalPath.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(diskPathSizeInBytes)), uintptr(unsafe.Pointer(buffer)))
|
||||
if r1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _openVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, openVirtualDiskFlags, parameters, handle)
|
||||
}
|
||||
|
||||
func _openVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(openVirtualDiskFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle)))
|
||||
if r1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
621
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
621
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
@ -19,6 +19,7 @@ const (
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
@ -26,7 +27,7 @@ var (
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return nil
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
@ -37,243 +38,62 @@ func errnoErr(e syscall.Errno) error {
|
||||
}
|
||||
|
||||
var (
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
||||
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
||||
|
||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
|
||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
||||
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
||||
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
|
||||
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
|
||||
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
|
||||
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
||||
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
||||
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
||||
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
||||
procLocalFree = modkernel32.NewProc("LocalFree")
|
||||
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
|
||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
||||
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
||||
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
||||
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||
procBackupRead = modkernel32.NewProc("BackupRead")
|
||||
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
||||
procLocalFree = modkernel32.NewProc("LocalFree")
|
||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
||||
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
|
||||
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
|
||||
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
|
||||
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
|
||||
procbind = modws2_32.NewProc("bind")
|
||||
)
|
||||
|
||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
||||
newport = syscall.Handle(r0)
|
||||
if newport == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
|
||||
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
||||
var _p0 uint32
|
||||
if wait {
|
||||
if releaseAll {
|
||||
_p0 = 1
|
||||
} else {
|
||||
_p0 = 0
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
|
||||
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||
success = r0 != 0
|
||||
if true {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
||||
}
|
||||
|
||||
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||
}
|
||||
|
||||
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
|
||||
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
|
||||
ptr = uintptr(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
|
||||
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
|
||||
status = ntstatus(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func rtlNtStatusToDosError(status ntstatus) (winerr error) {
|
||||
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
|
||||
if r0 != 0 {
|
||||
winerr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
|
||||
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
|
||||
status = ntstatus(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
|
||||
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
|
||||
status = ntstatus(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(accountName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
|
||||
}
|
||||
|
||||
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -281,11 +101,7 @@ func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidS
|
||||
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -302,126 +118,73 @@ func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision ui
|
||||
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func localFree(mem uintptr) {
|
||||
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
|
||||
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
|
||||
len = uint32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
||||
var _p0 uint32
|
||||
if releaseAll {
|
||||
_p0 = 1
|
||||
} else {
|
||||
_p0 = 0
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||
success = r0 != 0
|
||||
if true {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func impersonateSelf(level uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func revertToSelf() (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
||||
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(accountName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
|
||||
}
|
||||
|
||||
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
||||
var _p0 uint32
|
||||
if openAsSelf {
|
||||
_p0 = 1
|
||||
} else {
|
||||
_p0 = 0
|
||||
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
||||
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
|
||||
}
|
||||
|
||||
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getCurrentThread() (h syscall.Handle) {
|
||||
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
||||
h = syscall.Handle(r0)
|
||||
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupPrivilegeName(_p0, luid, buffer, size)
|
||||
}
|
||||
|
||||
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -442,53 +205,27 @@ func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err err
|
||||
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
||||
var _p0 uint32
|
||||
if openAsSelf {
|
||||
_p0 = 1
|
||||
}
|
||||
return _lookupPrivilegeName(_p0, luid, buffer, size)
|
||||
}
|
||||
|
||||
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
|
||||
}
|
||||
|
||||
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
|
||||
func revertToSelf() (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -501,22 +238,14 @@ func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, proce
|
||||
var _p1 uint32
|
||||
if abort {
|
||||
_p1 = 1
|
||||
} else {
|
||||
_p1 = 0
|
||||
}
|
||||
var _p2 uint32
|
||||
if processSecurity {
|
||||
_p2 = 1
|
||||
} else {
|
||||
_p2 = 0
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -529,22 +258,162 @@ func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, p
|
||||
var _p1 uint32
|
||||
if abort {
|
||||
_p1 = 1
|
||||
} else {
|
||||
_p1 = 0
|
||||
}
|
||||
var _p2 uint32
|
||||
if processSecurity {
|
||||
_p2 = 1
|
||||
} else {
|
||||
_p2 = 0
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||
}
|
||||
|
||||
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
||||
newport = syscall.Handle(r0)
|
||||
if newport == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
||||
}
|
||||
|
||||
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getCurrentThread() (h syscall.Handle) {
|
||||
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
||||
h = syscall.Handle(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
|
||||
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
|
||||
ptr = uintptr(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func localFree(mem uintptr) {
|
||||
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
|
||||
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
|
||||
status = ntstatus(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
|
||||
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
|
||||
status = ntstatus(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
|
||||
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
|
||||
status = ntstatus(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func rtlNtStatusToDosError(status ntstatus) (winerr error) {
|
||||
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
|
||||
if r0 != 0 {
|
||||
winerr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
|
||||
var _p0 uint32
|
||||
if wait {
|
||||
_p0 = 1
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -552,11 +421,7 @@ func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, p
|
||||
func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
|
||||
if r1 == socketError {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user