mirror of
https://github.com/docker/cli.git
synced 2026-11-13 08:13:40 -04:00
Compare commits
53
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a72d7cdbeb | ||
|
|
400f12f7a5 | ||
|
|
6eb67bf572 | ||
|
|
5bab750c2b | ||
|
|
5650f9b102 | ||
|
|
e8bc27d4ca | ||
|
|
2acb9c2e8f | ||
|
|
230d4d0bab | ||
|
|
6d47c06b0d | ||
|
|
a6d757c0e3 | ||
|
|
c463d968fc | ||
|
|
a61a0c3ac9 | ||
|
|
0576b3dad7 | ||
|
|
60b13f1f80 | ||
|
|
5dab29765f | ||
|
|
b600054c51 | ||
|
|
42912d4d68 | ||
|
|
28638aab95 | ||
|
|
e89982aea9 | ||
|
|
dafe63b085 | ||
|
|
7dfc99c17e | ||
|
|
079107e1bc | ||
|
|
14d7485d98 | ||
|
|
50bb59277a | ||
|
|
c4767b3141 | ||
|
|
3f807ede91 | ||
|
|
836fc57e2a | ||
|
|
258e27438f | ||
|
|
29cd8591ee | ||
|
|
e23a63eb4c | ||
|
|
1e52a86d42 | ||
|
|
e382a5f225 | ||
|
|
b72abbb6f0 | ||
|
|
26e9ff49c8 | ||
|
|
4021ee92fe | ||
|
|
326c7138bb | ||
|
|
ee359a394b | ||
|
|
004e2925d7 | ||
|
|
6691085012 | ||
|
|
60f2d38d53 | ||
|
|
81b482ea5e | ||
|
|
211e74b240 | ||
|
|
8beff78d85 | ||
|
|
e64914c890 | ||
|
|
c1d70d1fbb | ||
|
|
53a3f0be18 | ||
|
|
4add46d686 | ||
|
|
ccea7d8a30 | ||
|
|
4cf5afaefa | ||
|
|
6c2b06d535 | ||
|
|
1c6a8ecf2e | ||
|
|
6d1c387af2 | ||
|
|
1e6db5d24b |
@@ -77,13 +77,13 @@ jobs:
|
|||||||
platformPair=${platform//\//-}
|
platformPair=${platform//\//-}
|
||||||
tar -cvzf "/tmp/out/docker-${platformPair}.tar.gz" .
|
tar -cvzf "/tmp/out/docker-${platformPair}.tar.gz" .
|
||||||
if [ -z "${{ matrix.use_glibc }}" ]; then
|
if [ -z "${{ matrix.use_glibc }}" ]; then
|
||||||
echo "ARTIFACT_NAME=${{ matrix.target }}" >> $GITHUB_ENV
|
echo "ARTIFACT_NAME=${{ matrix.target }}-${platformPair}" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo "ARTIFACT_NAME=${{ matrix.target }}-glibc" >> $GITHUB_ENV
|
echo "ARTIFACT_NAME=${{ matrix.target }}-${platformPair}-glibc" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
-
|
-
|
||||||
name: Upload artifacts
|
name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.ARTIFACT_NAME }}
|
name: ${{ env.ARTIFACT_NAME }}
|
||||||
path: /tmp/out/*
|
path: /tmp/out/*
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ jobs:
|
|||||||
name: Set up Go
|
name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.21.9
|
go-version: 1.21.12
|
||||||
-
|
-
|
||||||
name: Test
|
name: Test
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
ARG BASE_VARIANT=alpine
|
ARG BASE_VARIANT=alpine
|
||||||
ARG ALPINE_VERSION=3.18
|
ARG ALPINE_VERSION=3.20
|
||||||
ARG BASE_DEBIAN_DISTRO=bookworm
|
ARG BASE_DEBIAN_DISTRO=bookworm
|
||||||
|
|
||||||
ARG GO_VERSION=1.21.9
|
ARG GO_VERSION=1.21.12
|
||||||
ARG XX_VERSION=1.4.0
|
ARG XX_VERSION=1.4.0
|
||||||
ARG GOVERSIONINFO_VERSION=v1.3.0
|
ARG GOVERSIONINFO_VERSION=v1.3.0
|
||||||
ARG GOTESTSUM_VERSION=v1.10.0
|
ARG GOTESTSUM_VERSION=v1.10.0
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli-plugins/hooks"
|
"github.com/docker/cli/cli-plugins/hooks"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
@@ -92,11 +93,35 @@ func invokeAndCollectHooks(dockerCli command.Cli, rootCmd, subCmd *cobra.Command
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
nextSteps = append(nextSteps, processedHook...)
|
|
||||||
|
var appended bool
|
||||||
|
nextSteps, appended = appendNextSteps(nextSteps, processedHook)
|
||||||
|
if !appended {
|
||||||
|
logrus.Debugf("Plugin %s responded with an empty hook message %q. Ignoring.", pluginName, string(hookReturn))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nextSteps
|
return nextSteps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appendNextSteps appends the processed hook output to the nextSteps slice.
|
||||||
|
// If the processed hook output is empty, it is not appended.
|
||||||
|
// Empty lines are not stripped if there's at least one non-empty line.
|
||||||
|
func appendNextSteps(nextSteps []string, processed []string) ([]string, bool) {
|
||||||
|
empty := true
|
||||||
|
for _, l := range processed {
|
||||||
|
if strings.TrimSpace(l) != "" {
|
||||||
|
empty = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if empty {
|
||||||
|
return nextSteps, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(nextSteps, processed...), true
|
||||||
|
}
|
||||||
|
|
||||||
// pluginMatch takes a plugin configuration and a string representing the
|
// pluginMatch takes a plugin configuration and a string representing the
|
||||||
// command being executed (such as 'image ls' – the root 'docker' is omitted)
|
// command being executed (such as 'image ls' – the root 'docker' is omitted)
|
||||||
// and, if the configuration includes a hook for the invoked command, returns
|
// and, if the configuration includes a hook for the invoked command, returns
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
|
is "gotest.tools/v3/assert/cmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetNaiveFlags(t *testing.T) {
|
func TestGetNaiveFlags(t *testing.T) {
|
||||||
@@ -108,3 +109,35 @@ func TestPluginMatch(t *testing.T) {
|
|||||||
assert.Equal(t, match, tc.expectedMatch)
|
assert.Equal(t, match, tc.expectedMatch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAppendNextSteps(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
processed []string
|
||||||
|
expectedOut []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
processed: []string{},
|
||||||
|
expectedOut: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processed: []string{"", ""},
|
||||||
|
expectedOut: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processed: []string{"Some hint", "", "Some other hint"},
|
||||||
|
expectedOut: []string{"Some hint", "", "Some other hint"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processed: []string{"Hint 1", "Hint 2"},
|
||||||
|
expectedOut: []string{"Hint 1", "Hint 2"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
got, appended := appendNextSteps([]string{}, tc.processed)
|
||||||
|
assert.Check(t, is.DeepEqual(got, tc.expectedOut))
|
||||||
|
assert.Check(t, is.Equal(appended, len(got) > 0))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -240,8 +240,7 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command
|
|||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = append(cmd.Environ(), ReexecEnvvar+"="+os.Args[0])
|
||||||
cmd.Env = append(cmd.Env, ReexecEnvvar+"="+os.Args[0])
|
|
||||||
cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin)
|
cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin)
|
||||||
|
|
||||||
return cmd, nil
|
return cmd, nil
|
||||||
|
|||||||
@@ -52,6 +52,24 @@ func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager
|
|||||||
opts = append(opts, withPluginClientConn(plugin.Name()))
|
opts = append(opts, withPluginClientConn(plugin.Name()))
|
||||||
}
|
}
|
||||||
err = tcmd.Initialize(opts...)
|
err = tcmd.Initialize(opts...)
|
||||||
|
ogRunE := cmd.RunE
|
||||||
|
if ogRunE == nil {
|
||||||
|
ogRun := cmd.Run
|
||||||
|
// necessary because error will always be nil here
|
||||||
|
// see: https://github.com/golangci/golangci-lint/issues/1379
|
||||||
|
//nolint:unparam
|
||||||
|
ogRunE = func(cmd *cobra.Command, args []string) error {
|
||||||
|
ogRun(cmd, args)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cmd.Run = nil
|
||||||
|
}
|
||||||
|
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||||
|
stopInstrumentation := dockerCli.StartInstrumentation(cmd)
|
||||||
|
err := ogRunE(cmd, args)
|
||||||
|
stopInstrumentation(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,6 +273,11 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption)
|
|||||||
return ResolveDefaultContext(cli.options, cli.contextStoreConfig)
|
return ResolveDefaultContext(cli.options, cli.contextStoreConfig)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(krissetto): pass ctx to the funcs instead of using this
|
||||||
|
cli.createGlobalMeterProvider(cli.baseCtx)
|
||||||
|
cli.createGlobalTracerProvider(cli.baseCtx)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
"github.com/containerd/platforms"
|
||||||
"github.com/distribution/reference"
|
"github.com/distribution/reference"
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
@@ -239,7 +239,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
|||||||
if options.platform != "" && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.41") {
|
if options.platform != "" && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.41") {
|
||||||
p, err := platforms.Parse(options.platform)
|
p, err := platforms.Parse(options.platform)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "error parsing specified platform")
|
return "", errors.Wrap(errdefs.InvalidParameter(err), "error parsing specified platform")
|
||||||
}
|
}
|
||||||
platform = &p
|
platform = &p
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,7 +186,11 @@ func runContainer(ctx context.Context, dockerCli command.Cli, runOpts *runOption
|
|||||||
defer closeFn()
|
defer closeFn()
|
||||||
}
|
}
|
||||||
|
|
||||||
statusChan := waitExitOrRemoved(ctx, apiClient, containerID, copts.autoRemove)
|
// New context here because we don't to cancel waiting on container exit/remove
|
||||||
|
// when we cancel attach, etc.
|
||||||
|
statusCtx, cancelStatusCtx := context.WithCancel(context.WithoutCancel(ctx))
|
||||||
|
defer cancelStatusCtx()
|
||||||
|
statusChan := waitExitOrRemoved(statusCtx, apiClient, containerID, copts.autoRemove)
|
||||||
|
|
||||||
// start the container
|
// start the container
|
||||||
if err := apiClient.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil {
|
if err := apiClient.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ func waitExitOrRemoved(ctx context.Context, apiClient client.APIClient, containe
|
|||||||
|
|
||||||
statusC := make(chan int)
|
statusC := make(chan int)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer close(statusC)
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ type CreateOptions struct {
|
|||||||
Description string
|
Description string
|
||||||
Docker map[string]string
|
Docker map[string]string
|
||||||
From string
|
From string
|
||||||
|
|
||||||
|
// Additional Metadata to store in the context. This option is not
|
||||||
|
// currently exposed to the user.
|
||||||
|
metaData map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
func longCreateDescription() string {
|
func longCreateDescription() string {
|
||||||
@@ -94,7 +98,8 @@ func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error {
|
|||||||
docker.DockerEndpoint: dockerEP,
|
docker.DockerEndpoint: dockerEP,
|
||||||
},
|
},
|
||||||
Metadata: command.DockerContext{
|
Metadata: command.DockerContext{
|
||||||
Description: o.Description,
|
Description: o.Description,
|
||||||
|
AdditionalFields: o.metaData,
|
||||||
},
|
},
|
||||||
Name: o.Name,
|
Name: o.Name,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,18 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/streams"
|
"github.com/docker/cli/cli/streams"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
|
is "gotest.tools/v3/assert/cmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExportImportWithFile(t *testing.T) {
|
func TestExportImportWithFile(t *testing.T) {
|
||||||
contextFile := filepath.Join(t.TempDir(), "exported")
|
contextFile := filepath.Join(t.TempDir(), "exported")
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "test")
|
createTestContext(t, cli, "test", map[string]any{
|
||||||
|
"MyCustomMetadata": t.Name(),
|
||||||
|
})
|
||||||
cli.ErrBuffer().Reset()
|
cli.ErrBuffer().Reset()
|
||||||
assert.NilError(t, RunExport(cli, &ExportOptions{
|
assert.NilError(t, RunExport(cli, &ExportOptions{
|
||||||
ContextName: "test",
|
ContextName: "test",
|
||||||
@@ -29,18 +33,26 @@ func TestExportImportWithFile(t *testing.T) {
|
|||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
context2, err := cli.ContextStore().GetMetadata("test2")
|
context2, err := cli.ContextStore().GetMetadata("test2")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.DeepEqual(t, context1.Endpoints, context2.Endpoints)
|
|
||||||
assert.DeepEqual(t, context1.Metadata, context2.Metadata)
|
|
||||||
assert.Equal(t, "test", context1.Name)
|
|
||||||
assert.Equal(t, "test2", context2.Name)
|
|
||||||
|
|
||||||
assert.Equal(t, "test2\n", cli.OutBuffer().String())
|
assert.Check(t, is.DeepEqual(context1.Metadata, command.DockerContext{
|
||||||
assert.Equal(t, "Successfully imported context \"test2\"\n", cli.ErrBuffer().String())
|
Description: "description of test",
|
||||||
|
AdditionalFields: map[string]any{"MyCustomMetadata": t.Name()},
|
||||||
|
}))
|
||||||
|
|
||||||
|
assert.Check(t, is.DeepEqual(context1.Endpoints, context2.Endpoints))
|
||||||
|
assert.Check(t, is.DeepEqual(context1.Metadata, context2.Metadata))
|
||||||
|
assert.Check(t, is.Equal("test", context1.Name))
|
||||||
|
assert.Check(t, is.Equal("test2", context2.Name))
|
||||||
|
|
||||||
|
assert.Check(t, is.Equal("test2\n", cli.OutBuffer().String()))
|
||||||
|
assert.Check(t, is.Equal("Successfully imported context \"test2\"\n", cli.ErrBuffer().String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExportImportPipe(t *testing.T) {
|
func TestExportImportPipe(t *testing.T) {
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "test")
|
createTestContext(t, cli, "test", map[string]any{
|
||||||
|
"MyCustomMetadata": t.Name(),
|
||||||
|
})
|
||||||
cli.ErrBuffer().Reset()
|
cli.ErrBuffer().Reset()
|
||||||
cli.OutBuffer().Reset()
|
cli.OutBuffer().Reset()
|
||||||
assert.NilError(t, RunExport(cli, &ExportOptions{
|
assert.NilError(t, RunExport(cli, &ExportOptions{
|
||||||
@@ -56,13 +68,19 @@ func TestExportImportPipe(t *testing.T) {
|
|||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
context2, err := cli.ContextStore().GetMetadata("test2")
|
context2, err := cli.ContextStore().GetMetadata("test2")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.DeepEqual(t, context1.Endpoints, context2.Endpoints)
|
|
||||||
assert.DeepEqual(t, context1.Metadata, context2.Metadata)
|
|
||||||
assert.Equal(t, "test", context1.Name)
|
|
||||||
assert.Equal(t, "test2", context2.Name)
|
|
||||||
|
|
||||||
assert.Equal(t, "test2\n", cli.OutBuffer().String())
|
assert.Check(t, is.DeepEqual(context1.Metadata, command.DockerContext{
|
||||||
assert.Equal(t, "Successfully imported context \"test2\"\n", cli.ErrBuffer().String())
|
Description: "description of test",
|
||||||
|
AdditionalFields: map[string]any{"MyCustomMetadata": t.Name()},
|
||||||
|
}))
|
||||||
|
|
||||||
|
assert.Check(t, is.DeepEqual(context1.Endpoints, context2.Endpoints))
|
||||||
|
assert.Check(t, is.DeepEqual(context1.Metadata, context2.Metadata))
|
||||||
|
assert.Check(t, is.Equal("test", context1.Name))
|
||||||
|
assert.Check(t, is.Equal("test2", context2.Name))
|
||||||
|
|
||||||
|
assert.Check(t, is.Equal("test2\n", cli.OutBuffer().String()))
|
||||||
|
assert.Check(t, is.Equal("Successfully imported context \"test2\"\n", cli.ErrBuffer().String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExportExistingFile(t *testing.T) {
|
func TestExportExistingFile(t *testing.T) {
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import (
|
|||||||
|
|
||||||
func TestInspect(t *testing.T) {
|
func TestInspect(t *testing.T) {
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "current")
|
createTestContext(t, cli, "current", map[string]any{
|
||||||
|
"MyCustomMetadata": "MyCustomMetadataValue",
|
||||||
|
})
|
||||||
cli.OutBuffer().Reset()
|
cli.OutBuffer().Reset()
|
||||||
assert.NilError(t, runInspect(cli, inspectOptions{
|
assert.NilError(t, runInspect(cli, inspectOptions{
|
||||||
refs: []string{"current"},
|
refs: []string{"current"},
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||||
|
//go:build go1.19
|
||||||
|
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -66,6 +69,8 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
|
|||||||
Name: rawMeta.Name,
|
Name: rawMeta.Name,
|
||||||
Current: isCurrent,
|
Current: isCurrent,
|
||||||
Error: err.Error(),
|
Error: err.Error(),
|
||||||
|
|
||||||
|
ContextType: getContextType(nil, opts.format),
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -80,6 +85,8 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
|
|||||||
Description: meta.Description,
|
Description: meta.Description,
|
||||||
DockerEndpoint: dockerEndpoint.Host,
|
DockerEndpoint: dockerEndpoint.Host,
|
||||||
Error: errMsg,
|
Error: errMsg,
|
||||||
|
|
||||||
|
ContextType: getContextType(meta.AdditionalFields, opts.format),
|
||||||
}
|
}
|
||||||
contexts = append(contexts, &desc)
|
contexts = append(contexts, &desc)
|
||||||
}
|
}
|
||||||
@@ -96,6 +103,8 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
|
|||||||
Name: curContext,
|
Name: curContext,
|
||||||
Current: true,
|
Current: true,
|
||||||
Error: errMsg,
|
Error: errMsg,
|
||||||
|
|
||||||
|
ContextType: getContextType(nil, opts.format),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sort.Slice(contexts, func(i, j int) bool {
|
sort.Slice(contexts, func(i, j int) bool {
|
||||||
@@ -111,6 +120,30 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getContextType sets the LegacyContextType field for compatibility with
|
||||||
|
// Visual Studio, which depends on this field from the "cloud integration"
|
||||||
|
// wrapper.
|
||||||
|
//
|
||||||
|
// https://github.com/docker/compose-cli/blob/c156ce6da4c2b317174d42daf1b019efa87e9f92/api/context/store/contextmetadata.go#L28-L34
|
||||||
|
// https://github.com/docker/compose-cli/blob/c156ce6da4c2b317174d42daf1b019efa87e9f92/api/context/store/store.go#L34-L51
|
||||||
|
//
|
||||||
|
// TODO(thaJeztah): remove this and [ClientContext.ContextType] once Visual Studio is updated to no longer depend on this.
|
||||||
|
func getContextType(meta map[string]any, format string) string {
|
||||||
|
if format != formatter.JSONFormat && format != formatter.JSONFormatKey {
|
||||||
|
// We only need the ContextType field when formatting as JSON,
|
||||||
|
// which is the format-string used by Visual Studio to detect the
|
||||||
|
// context-type.
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if ct, ok := meta["Type"]; ok {
|
||||||
|
// If the context on-disk has a context-type (ecs, aci), return it.
|
||||||
|
return ct.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the default context-type.
|
||||||
|
return "moby"
|
||||||
|
}
|
||||||
|
|
||||||
func format(dockerCli command.Cli, opts *listOptions, contexts []*formatter.ClientContext) error {
|
func format(dockerCli command.Cli, opts *listOptions, contexts []*formatter.ClientContext) error {
|
||||||
contextCtx := formatter.Context{
|
contextCtx := formatter.Context{
|
||||||
Output: dockerCli.Out(),
|
Output: dockerCli.Out(),
|
||||||
|
|||||||
@@ -4,36 +4,70 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/docker/cli/cli/command/formatter"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
"gotest.tools/v3/golden"
|
"gotest.tools/v3/golden"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createTestContext(t *testing.T, cli command.Cli, name string) {
|
func createTestContexts(t *testing.T, cli command.Cli, name ...string) {
|
||||||
|
t.Helper()
|
||||||
|
for _, n := range name {
|
||||||
|
createTestContext(t, cli, n, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestContext(t *testing.T, cli command.Cli, name string, metaData map[string]any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
err := RunCreate(cli, &CreateOptions{
|
err := RunCreate(cli, &CreateOptions{
|
||||||
Name: name,
|
Name: name,
|
||||||
Description: "description of " + name,
|
Description: "description of " + name,
|
||||||
Docker: map[string]string{keyHost: "https://someswarmserver.example.com"},
|
Docker: map[string]string{keyHost: "https://someswarmserver.example.com"},
|
||||||
|
|
||||||
|
metaData: metaData,
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
func TestList(t *testing.T) {
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "current")
|
createTestContexts(t, cli, "current", "other", "unset")
|
||||||
createTestContext(t, cli, "other")
|
|
||||||
createTestContext(t, cli, "unset")
|
|
||||||
cli.SetCurrentContext("current")
|
cli.SetCurrentContext("current")
|
||||||
cli.OutBuffer().Reset()
|
cli.OutBuffer().Reset()
|
||||||
assert.NilError(t, runList(cli, &listOptions{}))
|
assert.NilError(t, runList(cli, &listOptions{}))
|
||||||
golden.Assert(t, cli.OutBuffer().String(), "list.golden")
|
golden.Assert(t, cli.OutBuffer().String(), "list.golden")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListJSON(t *testing.T) {
|
||||||
|
cli := makeFakeCli(t)
|
||||||
|
createTestContext(t, cli, "current", nil)
|
||||||
|
createTestContext(t, cli, "context1", map[string]any{"Type": "aci"})
|
||||||
|
createTestContext(t, cli, "context2", map[string]any{"Type": "ecs"})
|
||||||
|
createTestContext(t, cli, "context3", map[string]any{"Type": "moby"})
|
||||||
|
cli.SetCurrentContext("current")
|
||||||
|
|
||||||
|
t.Run("format={{json .}}", func(t *testing.T) {
|
||||||
|
cli.OutBuffer().Reset()
|
||||||
|
assert.NilError(t, runList(cli, &listOptions{format: formatter.JSONFormat}))
|
||||||
|
golden.Assert(t, cli.OutBuffer().String(), "list-json.golden")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("format=json", func(t *testing.T) {
|
||||||
|
cli.OutBuffer().Reset()
|
||||||
|
assert.NilError(t, runList(cli, &listOptions{format: formatter.JSONFormatKey}))
|
||||||
|
golden.Assert(t, cli.OutBuffer().String(), "list-json.golden")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("format={{ json .Name }}", func(t *testing.T) {
|
||||||
|
cli.OutBuffer().Reset()
|
||||||
|
assert.NilError(t, runList(cli, &listOptions{format: `{{ json .Name }}`}))
|
||||||
|
golden.Assert(t, cli.OutBuffer().String(), "list-json-name.golden")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestListQuiet(t *testing.T) {
|
func TestListQuiet(t *testing.T) {
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "current")
|
createTestContexts(t, cli, "current", "other")
|
||||||
createTestContext(t, cli, "other")
|
|
||||||
cli.SetCurrentContext("current")
|
cli.SetCurrentContext("current")
|
||||||
cli.OutBuffer().Reset()
|
cli.OutBuffer().Reset()
|
||||||
assert.NilError(t, runList(cli, &listOptions{quiet: true}))
|
assert.NilError(t, runList(cli, &listOptions{quiet: true}))
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import (
|
|||||||
|
|
||||||
func TestRemove(t *testing.T) {
|
func TestRemove(t *testing.T) {
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "current")
|
createTestContexts(t, cli, "current", "other")
|
||||||
createTestContext(t, cli, "other")
|
|
||||||
assert.NilError(t, RunRemove(cli, RemoveOptions{}, []string{"other"}))
|
assert.NilError(t, RunRemove(cli, RemoveOptions{}, []string{"other"}))
|
||||||
_, err := cli.ContextStore().GetMetadata("current")
|
_, err := cli.ContextStore().GetMetadata("current")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
@@ -24,8 +23,7 @@ func TestRemove(t *testing.T) {
|
|||||||
|
|
||||||
func TestRemoveNotAContext(t *testing.T) {
|
func TestRemoveNotAContext(t *testing.T) {
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "current")
|
createTestContexts(t, cli, "current", "other")
|
||||||
createTestContext(t, cli, "other")
|
|
||||||
err := RunRemove(cli, RemoveOptions{}, []string{"not-a-context"})
|
err := RunRemove(cli, RemoveOptions{}, []string{"not-a-context"})
|
||||||
assert.ErrorContains(t, err, `context "not-a-context" does not exist`)
|
assert.ErrorContains(t, err, `context "not-a-context" does not exist`)
|
||||||
|
|
||||||
@@ -35,8 +33,7 @@ func TestRemoveNotAContext(t *testing.T) {
|
|||||||
|
|
||||||
func TestRemoveCurrent(t *testing.T) {
|
func TestRemoveCurrent(t *testing.T) {
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "current")
|
createTestContexts(t, cli, "current", "other")
|
||||||
createTestContext(t, cli, "other")
|
|
||||||
cli.SetCurrentContext("current")
|
cli.SetCurrentContext("current")
|
||||||
err := RunRemove(cli, RemoveOptions{}, []string{"current"})
|
err := RunRemove(cli, RemoveOptions{}, []string{"current"})
|
||||||
assert.ErrorContains(t, err, `context "current" is in use, set -f flag to force remove`)
|
assert.ErrorContains(t, err, `context "current" is in use, set -f flag to force remove`)
|
||||||
@@ -50,8 +47,7 @@ func TestRemoveCurrentForce(t *testing.T) {
|
|||||||
assert.NilError(t, testCfg.Save())
|
assert.NilError(t, testCfg.Save())
|
||||||
|
|
||||||
cli := makeFakeCli(t, withCliConfig(testCfg))
|
cli := makeFakeCli(t, withCliConfig(testCfg))
|
||||||
createTestContext(t, cli, "current")
|
createTestContexts(t, cli, "current", "other")
|
||||||
createTestContext(t, cli, "other")
|
|
||||||
cli.SetCurrentContext("current")
|
cli.SetCurrentContext("current")
|
||||||
assert.NilError(t, RunRemove(cli, RemoveOptions{Force: true}, []string{"current"}))
|
assert.NilError(t, RunRemove(cli, RemoveOptions{Force: true}, []string{"current"}))
|
||||||
reloadedConfig, err := config.Load(configDir)
|
reloadedConfig, err := config.Load(configDir)
|
||||||
@@ -61,7 +57,7 @@ func TestRemoveCurrentForce(t *testing.T) {
|
|||||||
|
|
||||||
func TestRemoveDefault(t *testing.T) {
|
func TestRemoveDefault(t *testing.T) {
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "other")
|
createTestContext(t, cli, "other", nil)
|
||||||
cli.SetCurrentContext("current")
|
cli.SetCurrentContext("current")
|
||||||
err := RunRemove(cli, RemoveOptions{}, []string{"default"})
|
err := RunRemove(cli, RemoveOptions{}, []string{"default"})
|
||||||
assert.ErrorContains(t, err, `default: context "default" cannot be removed`)
|
assert.ErrorContains(t, err, `default: context "default" cannot be removed`)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
func TestShow(t *testing.T) {
|
func TestShow(t *testing.T) {
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "current")
|
createTestContext(t, cli, "current", nil)
|
||||||
cli.SetCurrentContext("current")
|
cli.SetCurrentContext("current")
|
||||||
|
|
||||||
cli.OutBuffer().Reset()
|
cli.OutBuffer().Reset()
|
||||||
|
|||||||
+2
-1
@@ -2,7 +2,8 @@
|
|||||||
{
|
{
|
||||||
"Name": "current",
|
"Name": "current",
|
||||||
"Metadata": {
|
"Metadata": {
|
||||||
"Description": "description of current"
|
"Description": "description of current",
|
||||||
|
"MyCustomMetadata": "MyCustomMetadataValue"
|
||||||
},
|
},
|
||||||
"Endpoints": {
|
"Endpoints": {
|
||||||
"docker": {
|
"docker": {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
"context1"
|
||||||
|
"context2"
|
||||||
|
"context3"
|
||||||
|
"current"
|
||||||
|
"default"
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{"Name":"context1","Description":"description of context1","DockerEndpoint":"https://someswarmserver.example.com","Current":false,"Error":"","ContextType":"aci"}
|
||||||
|
{"Name":"context2","Description":"description of context2","DockerEndpoint":"https://someswarmserver.example.com","Current":false,"Error":"","ContextType":"ecs"}
|
||||||
|
{"Name":"context3","Description":"description of context3","DockerEndpoint":"https://someswarmserver.example.com","Current":false,"Error":"","ContextType":"moby"}
|
||||||
|
{"Name":"current","Description":"description of current","DockerEndpoint":"https://someswarmserver.example.com","Current":true,"Error":"","ContextType":"moby"}
|
||||||
|
{"Name":"default","Description":"Current DOCKER_HOST based configuration","DockerEndpoint":"unix:///var/run/docker.sock","Current":false,"Error":"","ContextType":"moby"}
|
||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/context/docker"
|
"github.com/docker/cli/cli/context/docker"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
"gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUpdateDescriptionOnly(t *testing.T) {
|
func TestUpdateDescriptionOnly(t *testing.T) {
|
||||||
@@ -34,7 +34,7 @@ func TestUpdateDescriptionOnly(t *testing.T) {
|
|||||||
|
|
||||||
func TestUpdateDockerOnly(t *testing.T) {
|
func TestUpdateDockerOnly(t *testing.T) {
|
||||||
cli := makeFakeCli(t)
|
cli := makeFakeCli(t)
|
||||||
createTestContext(t, cli, "test")
|
createTestContext(t, cli, "test", nil)
|
||||||
assert.NilError(t, RunUpdate(cli, &UpdateOptions{
|
assert.NilError(t, RunUpdate(cli, &UpdateOptions{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Docker: map[string]string{
|
Docker: map[string]string{
|
||||||
@@ -46,7 +46,7 @@ func TestUpdateDockerOnly(t *testing.T) {
|
|||||||
dc, err := command.GetDockerContext(c)
|
dc, err := command.GetDockerContext(c)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, dc.Description, "description of test")
|
assert.Equal(t, dc.Description, "description of test")
|
||||||
assert.Check(t, cmp.Contains(c.Endpoints, docker.DockerEndpoint))
|
assert.Check(t, is.Contains(c.Endpoints, docker.DockerEndpoint))
|
||||||
assert.Equal(t, c.Endpoints[docker.DockerEndpoint].(docker.EndpointMeta).Host, "tcp://some-host")
|
assert.Equal(t, c.Endpoints[docker.DockerEndpoint].(docker.EndpointMeta).Host, "tcp://some-host")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package formatter
|
package formatter
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ClientContextTableFormat is the default client context format.
|
// ClientContextTableFormat is the default client context format.
|
||||||
ClientContextTableFormat = "table {{.Name}}{{if .Current}} *{{end}}\t{{.Description}}\t{{.DockerEndpoint}}\t{{.Error}}"
|
ClientContextTableFormat = "table {{.Name}}{{if .Current}} *{{end}}\t{{.Description}}\t{{.DockerEndpoint}}\t{{.Error}}"
|
||||||
@@ -28,6 +30,13 @@ type ClientContext struct {
|
|||||||
DockerEndpoint string
|
DockerEndpoint string
|
||||||
Current bool
|
Current bool
|
||||||
Error string
|
Error string
|
||||||
|
|
||||||
|
// ContextType is a temporary field for compatibility with
|
||||||
|
// Visual Studio, which depends on this from the "cloud integration"
|
||||||
|
// wrapper.
|
||||||
|
//
|
||||||
|
// Deprecated: this type is only for backward-compatibility. Do not use.
|
||||||
|
ContextType string `json:"ContextType,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientContextWrite writes formatted contexts using the Context
|
// ClientContextWrite writes formatted contexts using the Context
|
||||||
@@ -60,6 +69,13 @@ func newClientContextContext() *clientContextContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientContextContext) MarshalJSON() ([]byte, error) {
|
func (c *clientContextContext) MarshalJSON() ([]byte, error) {
|
||||||
|
if c.c.ContextType != "" {
|
||||||
|
// We only have ContextType set for plain "json" or "{{json .}}" formatting,
|
||||||
|
// so we should be able to just use the default json.Marshal with no
|
||||||
|
// special handling.
|
||||||
|
return json.Marshal(c.c)
|
||||||
|
}
|
||||||
|
// FIXME(thaJeztah): why do we need a special marshal function here?
|
||||||
return MarshalJSON(c)
|
return MarshalJSON(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+41
-25
@@ -41,35 +41,25 @@ type TelemetryClient interface {
|
|||||||
// each time this function is invoked.
|
// each time this function is invoked.
|
||||||
Resource() *resource.Resource
|
Resource() *resource.Resource
|
||||||
|
|
||||||
// TracerProvider returns a TracerProvider. This TracerProvider will be configured
|
// TracerProvider returns the currently initialized TracerProvider. This TracerProvider will be configured
|
||||||
// with the default tracing components for a CLI program along with any options given
|
// with the default tracing components for a CLI program
|
||||||
// for the SDK.
|
TracerProvider() trace.TracerProvider
|
||||||
TracerProvider(ctx context.Context, opts ...sdktrace.TracerProviderOption) TracerProvider
|
|
||||||
|
|
||||||
// MeterProvider returns a MeterProvider. This MeterProvider will be configured
|
// MeterProvider returns the currently initialized MeterProvider. This MeterProvider will be configured
|
||||||
// with the default metric components for a CLI program along with any options given
|
// with the default metric components for a CLI program
|
||||||
// for the SDK.
|
MeterProvider() metric.MeterProvider
|
||||||
MeterProvider(ctx context.Context, opts ...sdkmetric.Option) MeterProvider
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) Resource() *resource.Resource {
|
func (cli *DockerCli) Resource() *resource.Resource {
|
||||||
return cli.res.Get()
|
return cli.res.Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) TracerProvider(ctx context.Context, opts ...sdktrace.TracerProviderOption) TracerProvider {
|
func (cli *DockerCli) TracerProvider() trace.TracerProvider {
|
||||||
allOpts := make([]sdktrace.TracerProviderOption, 0, len(opts)+2)
|
return otel.GetTracerProvider()
|
||||||
allOpts = append(allOpts, sdktrace.WithResource(cli.Resource()))
|
|
||||||
allOpts = append(allOpts, dockerSpanExporter(ctx, cli)...)
|
|
||||||
allOpts = append(allOpts, opts...)
|
|
||||||
return sdktrace.NewTracerProvider(allOpts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) MeterProvider(ctx context.Context, opts ...sdkmetric.Option) MeterProvider {
|
func (cli *DockerCli) MeterProvider() metric.MeterProvider {
|
||||||
allOpts := make([]sdkmetric.Option, 0, len(opts)+2)
|
return otel.GetMeterProvider()
|
||||||
allOpts = append(allOpts, sdkmetric.WithResource(cli.Resource()))
|
|
||||||
allOpts = append(allOpts, dockerMetricExporter(ctx, cli)...)
|
|
||||||
allOpts = append(allOpts, opts...)
|
|
||||||
return sdkmetric.NewMeterProvider(allOpts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithResourceOptions configures additional options for the default resource. The default
|
// WithResourceOptions configures additional options for the default resource. The default
|
||||||
@@ -122,6 +112,28 @@ func (r *telemetryResource) init() {
|
|||||||
r.opts = nil
|
r.opts = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createGlobalMeterProvider creates a new MeterProvider from the initialized DockerCli struct
|
||||||
|
// with the given options and sets it as the global meter provider
|
||||||
|
func (cli *DockerCli) createGlobalMeterProvider(ctx context.Context, opts ...sdkmetric.Option) {
|
||||||
|
allOpts := make([]sdkmetric.Option, 0, len(opts)+2)
|
||||||
|
allOpts = append(allOpts, sdkmetric.WithResource(cli.Resource()))
|
||||||
|
allOpts = append(allOpts, dockerMetricExporter(ctx, cli)...)
|
||||||
|
allOpts = append(allOpts, opts...)
|
||||||
|
mp := sdkmetric.NewMeterProvider(allOpts...)
|
||||||
|
otel.SetMeterProvider(mp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createGlobalTracerProvider creates a new TracerProvider from the initialized DockerCli struct
|
||||||
|
// with the given options and sets it as the global tracer provider
|
||||||
|
func (cli *DockerCli) createGlobalTracerProvider(ctx context.Context, opts ...sdktrace.TracerProviderOption) {
|
||||||
|
allOpts := make([]sdktrace.TracerProviderOption, 0, len(opts)+2)
|
||||||
|
allOpts = append(allOpts, sdktrace.WithResource(cli.Resource()))
|
||||||
|
allOpts = append(allOpts, dockerSpanExporter(ctx, cli)...)
|
||||||
|
allOpts = append(allOpts, opts...)
|
||||||
|
tp := sdktrace.NewTracerProvider(allOpts...)
|
||||||
|
otel.SetTracerProvider(tp)
|
||||||
|
}
|
||||||
|
|
||||||
func defaultResourceOptions() []resource.Option {
|
func defaultResourceOptions() []resource.Option {
|
||||||
return []resource.Option{
|
return []resource.Option{
|
||||||
resource.WithDetectors(serviceNameDetector{}),
|
resource.WithDetectors(serviceNameDetector{}),
|
||||||
@@ -174,11 +186,6 @@ func newCLIReader(exp sdkmetric.Exporter) sdkmetric.Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *cliReader) Shutdown(ctx context.Context) error {
|
func (r *cliReader) Shutdown(ctx context.Context) error {
|
||||||
var rm metricdata.ResourceMetrics
|
|
||||||
if err := r.Reader.Collect(ctx, &rm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Place a pretty tight constraint on the actual reporting.
|
// Place a pretty tight constraint on the actual reporting.
|
||||||
// We don't want CLI metrics to prevent the CLI from exiting
|
// We don't want CLI metrics to prevent the CLI from exiting
|
||||||
// so if there's some kind of issue we need to abort pretty
|
// so if there's some kind of issue we need to abort pretty
|
||||||
@@ -186,6 +193,15 @@ func (r *cliReader) Shutdown(ctx context.Context) error {
|
|||||||
ctx, cancel := context.WithTimeout(ctx, exportTimeout)
|
ctx, cancel := context.WithTimeout(ctx, exportTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
return r.ForceFlush(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *cliReader) ForceFlush(ctx context.Context) error {
|
||||||
|
var rm metricdata.ResourceMetrics
|
||||||
|
if err := r.Reader.Collect(ctx, &rm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return r.exporter.Export(ctx, &rm)
|
return r.exporter.Export(ctx, &rm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ func BaseCommandAttributes(cmd *cobra.Command, streams Streams) []attribute.KeyV
|
|||||||
// Note: this should be the last func to wrap/modify the PersistentRunE/RunE funcs before command execution.
|
// Note: this should be the last func to wrap/modify the PersistentRunE/RunE funcs before command execution.
|
||||||
//
|
//
|
||||||
// can also be used for spans!
|
// can also be used for spans!
|
||||||
func (cli *DockerCli) InstrumentCobraCommands(cmd *cobra.Command, mp metric.MeterProvider) {
|
func (cli *DockerCli) InstrumentCobraCommands(ctx context.Context, cmd *cobra.Command) {
|
||||||
meter := getDefaultMeter(mp)
|
|
||||||
// If PersistentPreRunE is nil, make it execute PersistentPreRun and return nil by default
|
// If PersistentPreRunE is nil, make it execute PersistentPreRun and return nil by default
|
||||||
ogPersistentPreRunE := cmd.PersistentPreRunE
|
ogPersistentPreRunE := cmd.PersistentPreRunE
|
||||||
if ogPersistentPreRunE == nil {
|
if ogPersistentPreRunE == nil {
|
||||||
@@ -55,10 +54,9 @@ func (cli *DockerCli) InstrumentCobraCommands(cmd *cobra.Command, mp metric.Mete
|
|||||||
}
|
}
|
||||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||||
// start the timer as the first step of every cobra command
|
// start the timer as the first step of every cobra command
|
||||||
baseAttrs := BaseCommandAttributes(cmd, cli)
|
stopInstrumentation := cli.StartInstrumentation(cmd)
|
||||||
stopCobraCmdTimer := startCobraCommandTimer(cmd, meter, baseAttrs)
|
|
||||||
cmdErr := ogRunE(cmd, args)
|
cmdErr := ogRunE(cmd, args)
|
||||||
stopCobraCmdTimer(cmdErr)
|
stopInstrumentation(cmdErr)
|
||||||
return cmdErr
|
return cmdErr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,8 +64,17 @@ func (cli *DockerCli) InstrumentCobraCommands(cmd *cobra.Command, mp metric.Mete
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCobraCommandTimer(cmd *cobra.Command, meter metric.Meter, attrs []attribute.KeyValue) func(err error) {
|
// StartInstrumentation instruments CLI commands with the individual metrics and spans configured.
|
||||||
ctx := cmd.Context()
|
// It's the main command OTel utility, and new command-related metrics should be added to it.
|
||||||
|
// It should be called immediately before command execution, and returns a stopInstrumentation function
|
||||||
|
// that must be called with the error resulting from the command execution.
|
||||||
|
func (cli *DockerCli) StartInstrumentation(cmd *cobra.Command) (stopInstrumentation func(error)) {
|
||||||
|
baseAttrs := BaseCommandAttributes(cmd, cli)
|
||||||
|
return startCobraCommandTimer(cli.MeterProvider(), baseAttrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func startCobraCommandTimer(mp metric.MeterProvider, attrs []attribute.KeyValue) func(err error) {
|
||||||
|
meter := getDefaultMeter(mp)
|
||||||
durationCounter, _ := meter.Float64Counter(
|
durationCounter, _ := meter.Float64Counter(
|
||||||
"command.time",
|
"command.time",
|
||||||
metric.WithDescription("Measures the duration of the cobra command"),
|
metric.WithDescription("Measures the duration of the cobra command"),
|
||||||
@@ -76,12 +83,20 @@ func startCobraCommandTimer(cmd *cobra.Command, meter metric.Meter, attrs []attr
|
|||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
return func(err error) {
|
return func(err error) {
|
||||||
|
// Use a new context for the export so that the command being cancelled
|
||||||
|
// doesn't affect the metrics, and we get metrics for cancelled commands.
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), exportTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
duration := float64(time.Since(start)) / float64(time.Millisecond)
|
duration := float64(time.Since(start)) / float64(time.Millisecond)
|
||||||
cmdStatusAttrs := attributesFromError(err)
|
cmdStatusAttrs := attributesFromError(err)
|
||||||
durationCounter.Add(ctx, duration,
|
durationCounter.Add(ctx, duration,
|
||||||
metric.WithAttributes(attrs...),
|
metric.WithAttributes(attrs...),
|
||||||
metric.WithAttributes(cmdStatusAttrs...),
|
metric.WithAttributes(cmdStatusAttrs...),
|
||||||
)
|
)
|
||||||
|
if mp, ok := mp.(MeterProvider); ok {
|
||||||
|
mp.ForceFlush(ctx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -307,10 +307,14 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mp := dockerCli.MeterProvider(ctx)
|
mp := dockerCli.MeterProvider()
|
||||||
defer mp.Shutdown(ctx)
|
if mp, ok := mp.(command.MeterProvider); ok {
|
||||||
otel.SetMeterProvider(mp)
|
defer mp.Shutdown(ctx)
|
||||||
dockerCli.InstrumentCobraCommands(cmd, mp)
|
} else {
|
||||||
|
fmt.Fprint(dockerCli.Err(), "Warning: Unexpected OTEL error, metrics may not be flushed")
|
||||||
|
}
|
||||||
|
|
||||||
|
dockerCli.InstrumentCobraCommands(ctx, cmd)
|
||||||
|
|
||||||
var envs []string
|
var envs []string
|
||||||
args, os.Args, envs, err = processAliases(dockerCli, cmd, args, os.Args)
|
args, os.Args, envs, err = processAliases(dockerCli, cmd, args, os.Args)
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
variable "GO_VERSION" {
|
variable "GO_VERSION" {
|
||||||
default = "1.21.9"
|
default = "1.21.12"
|
||||||
}
|
}
|
||||||
variable "VERSION" {
|
variable "VERSION" {
|
||||||
default = ""
|
default = ""
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
ARG ALPINE_VERSION=3.18
|
ARG ALPINE_VERSION=3.20
|
||||||
|
|
||||||
FROM alpine:${ALPINE_VERSION} AS gen
|
FROM alpine:${ALPINE_VERSION} AS gen
|
||||||
RUN apk add --no-cache bash git
|
RUN apk add --no-cache bash git
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
ARG GO_VERSION=1.21.9
|
ARG GO_VERSION=1.21.12
|
||||||
ARG ALPINE_VERSION=3.18
|
ARG ALPINE_VERSION=3.20
|
||||||
|
|
||||||
ARG BUILDX_VERSION=0.12.1
|
ARG BUILDX_VERSION=0.12.1
|
||||||
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
|
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
ARG GO_VERSION=1.21.9
|
ARG GO_VERSION=1.21.12
|
||||||
ARG ALPINE_VERSION=3.18
|
ARG ALPINE_VERSION=3.20
|
||||||
ARG GOLANGCI_LINT_VERSION=v1.55.2
|
ARG GOLANGCI_LINT_VERSION=v1.55.2
|
||||||
|
|
||||||
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint
|
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
ARG GO_VERSION=1.21.9
|
ARG GO_VERSION=1.21.12
|
||||||
ARG ALPINE_VERSION=3.18
|
ARG ALPINE_VERSION=3.20
|
||||||
ARG MODOUTDATED_VERSION=v0.8.0
|
ARG MODOUTDATED_VERSION=v0.8.0
|
||||||
|
|
||||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||||
|
|||||||
@@ -902,17 +902,19 @@ PS C:\> docker run --device=class/86E0D1E0-8089-11D0-9CE4-08003E301F73 mcr.micro
|
|||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
>
|
>
|
||||||
> This is experimental feature and as such doesn't represent a stable API.
|
> The CDI feature is experimental, and potentially subject to change.
|
||||||
|
> CDI is currently only supported for Linux containers.
|
||||||
|
|
||||||
Container Device Interface (CDI) is a
|
[Container Device Interface
|
||||||
[standardized](https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md)
|
(CDI)](https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md)
|
||||||
mechanism for container runtimes to create containers which are able to
|
is a standardized mechanism for container runtimes to create containers which
|
||||||
interact with third party devices.
|
are able to interact with third party devices.
|
||||||
|
|
||||||
With CDI, device configurations are defined using a JSON file. In addition to
|
With CDI, device configurations are declaratively defined using a JSON or YAML
|
||||||
enabling the container to interact with the device node, it also lets you
|
file. In addition to enabling the container to interact with the device node,
|
||||||
specify additional configuration for the device, such as kernel modules, host
|
it also lets you specify additional configuration for the device, such as
|
||||||
libraries, and environment variables.
|
environment variables, host mounts (such as shared objects), and executable
|
||||||
|
hooks.
|
||||||
|
|
||||||
You can reference a CDI device with the `--device` flag using the
|
You can reference a CDI device with the `--device` flag using the
|
||||||
fully-qualified name of the device, as shown in the following example:
|
fully-qualified name of the device, as shown in the following example:
|
||||||
@@ -924,10 +926,10 @@ $ docker run --device=vendor.com/class=device-name --rm -it ubuntu
|
|||||||
This starts an `ubuntu` container with access to the specified CDI device,
|
This starts an `ubuntu` container with access to the specified CDI device,
|
||||||
`vendor.com/class=device-name`, assuming that:
|
`vendor.com/class=device-name`, assuming that:
|
||||||
|
|
||||||
- A valid CDI specification (JSON file) for the requested device is available
|
- A valid CDI specification (JSON or YAML file) for the requested device is
|
||||||
on the system running the daemon, in one of the configured CDI specification
|
available on the system running the daemon, in one of the configured CDI
|
||||||
directories.
|
specification directories.
|
||||||
- The CDI feature has been enabled on the daemon side, see [Enable CDI
|
- The CDI feature has been enabled in the daemon; see [Enable CDI
|
||||||
devices](https://docs.docker.com/reference/cli/dockerd/#enable-cdi-devices).
|
devices](https://docs.docker.com/reference/cli/dockerd/#enable-cdi-devices).
|
||||||
|
|
||||||
### <a name="attach"></a> Attach to STDIN/STDOUT/STDERR (-a, --attach)
|
### <a name="attach"></a> Attach to STDIN/STDOUT/STDERR (-a, --attach)
|
||||||
|
|||||||
@@ -117,6 +117,12 @@ data traffic from the management traffic of the cluster.
|
|||||||
|
|
||||||
If unspecified, the IP address or interface of the advertise address is used.
|
If unspecified, the IP address or interface of the advertise address is used.
|
||||||
|
|
||||||
|
Setting `--data-path-addr` does not restrict which interfaces or source IP
|
||||||
|
addresses the VXLAN socket is bound to. Similar to `--advertise-addr`, the
|
||||||
|
purpose of this flag is to inform other members of the swarm about which
|
||||||
|
address to use for control plane traffic. To restrict access to the VXLAN port
|
||||||
|
of the node, use firewall rules.
|
||||||
|
|
||||||
### <a name="data-path-port"></a> Configure port number for data traffic (--data-path-port)
|
### <a name="data-path-port"></a> Configure port number for data traffic (--data-path-port)
|
||||||
|
|
||||||
The `--data-path-port` flag allows you to configure the UDP port number to use
|
The `--data-path-port` flag allows you to configure the UDP port number to use
|
||||||
|
|||||||
@@ -530,7 +530,7 @@ For example:
|
|||||||
"runtimeType": "io.containerd.runsc.v1",
|
"runtimeType": "io.containerd.runsc.v1",
|
||||||
"options": {
|
"options": {
|
||||||
"TypeUrl": "io.containerd.runsc.v1.options",
|
"TypeUrl": "io.containerd.runsc.v1.options",
|
||||||
"ConfigPath": "/etc/containerd/runsc.toml",
|
"ConfigPath": "/etc/containerd/runsc.toml"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/cli/e2e/internal/fixtures"
|
"github.com/docker/cli/e2e/internal/fixtures"
|
||||||
"github.com/docker/cli/internal/test/environment"
|
"github.com/docker/cli/internal/test/environment"
|
||||||
@@ -34,6 +35,22 @@ func TestRunAttachedFromRemoteImageAndRemove(t *testing.T) {
|
|||||||
golden.Assert(t, result.Stderr(), "run-attached-from-remote-and-remove.golden")
|
golden.Assert(t, result.Stderr(), "run-attached-from-remote-and-remove.golden")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for https://github.com/docker/cli/issues/5053
|
||||||
|
func TestRunInvalidEntrypointWithAutoremove(t *testing.T) {
|
||||||
|
environment.SkipIfDaemonNotLinux(t)
|
||||||
|
|
||||||
|
result := make(chan *icmd.Result)
|
||||||
|
go func() {
|
||||||
|
result <- icmd.RunCommand("docker", "run", "--rm", fixtures.AlpineImage, "invalidcommand")
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case r := <-result:
|
||||||
|
r.Assert(t, icmd.Expected{ExitCode: 127})
|
||||||
|
case <-time.After(4 * time.Second):
|
||||||
|
t.Fatal("test took too long, shouldn't hang")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunWithContentTrust(t *testing.T) {
|
func TestRunWithContentTrust(t *testing.T) {
|
||||||
skip.If(t, environment.RemoteDaemon())
|
skip.If(t, environment.RemoteDaemon())
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
ARG GO_VERSION=1.21.9
|
ARG GO_VERSION=1.21.12
|
||||||
|
|
||||||
FROM golang:${GO_VERSION}-alpine AS generated
|
FROM golang:${GO_VERSION}-alpine AS generated
|
||||||
ENV GOTOOLCHAIN=local
|
ENV GOTOOLCHAIN=local
|
||||||
|
|||||||
+7
-8
@@ -8,11 +8,11 @@ go 1.21
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0
|
dario.cat/mergo v1.0.0
|
||||||
github.com/containerd/containerd v1.7.15
|
github.com/containerd/platforms v0.2.0
|
||||||
github.com/creack/pty v1.1.21
|
github.com/creack/pty v1.1.21
|
||||||
github.com/distribution/reference v0.5.0
|
github.com/distribution/reference v0.5.0
|
||||||
github.com/docker/distribution v2.8.3+incompatible
|
github.com/docker/distribution v2.8.3+incompatible
|
||||||
github.com/docker/docker v26.0.1-0.20240422144514-c8af8ebe4a89+incompatible
|
github.com/docker/docker v26.1.4-0.20240605103321-de5c9cf0b96e+incompatible // 26.1 branch (v26.1.4-dev)
|
||||||
github.com/docker/docker-credential-helpers v0.8.1
|
github.com/docker/docker-credential-helpers v0.8.1
|
||||||
github.com/docker/go-connections v0.5.0
|
github.com/docker/go-connections v0.5.0
|
||||||
github.com/docker/go-units v0.5.0
|
github.com/docker/go-units v0.5.0
|
||||||
@@ -29,7 +29,7 @@ require (
|
|||||||
github.com/moby/term v0.5.0
|
github.com/moby/term v0.5.0
|
||||||
github.com/morikuni/aec v1.0.0
|
github.com/morikuni/aec v1.0.0
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc5
|
github.com/opencontainers/image-spec v1.1.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
@@ -50,16 +50,17 @@ require (
|
|||||||
golang.org/x/text v0.14.0
|
golang.org/x/text v0.14.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gotest.tools/v3 v3.5.1
|
gotest.tools/v3 v3.5.1
|
||||||
tags.cncf.io/container-device-interface v0.6.2
|
tags.cncf.io/container-device-interface v0.7.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/Microsoft/hcsshim v0.11.4 // indirect
|
github.com/Microsoft/hcsshim v0.11.5 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/containerd/containerd v1.7.18 // indirect
|
||||||
github.com/containerd/log v0.1.0 // indirect
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||||
@@ -89,10 +90,8 @@ require (
|
|||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||||
golang.org/x/crypto v0.21.0 // indirect
|
golang.org/x/crypto v0.21.0 // indirect
|
||||||
golang.org/x/mod v0.14.0 // indirect
|
|
||||||
golang.org/x/net v0.23.0 // indirect
|
golang.org/x/net v0.23.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.16.0 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||||
google.golang.org/grpc v1.60.1 // indirect
|
google.golang.org/grpc v1.60.1 // indirect
|
||||||
|
|||||||
+14
-16
@@ -6,10 +6,10 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl
|
|||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
|
github.com/Microsoft/hcsshim v0.11.5 h1:haEcLNpj9Ka1gd3B3tAEs9CpE0c+1IhoL59w/exYU38=
|
||||||
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
|
github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
|
||||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d h1:hi6J4K6DKrR4/ljxn6SF6nURyu785wKMuQcjt7H3VCQ=
|
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d h1:hi6J4K6DKrR4/ljxn6SF6nURyu785wKMuQcjt7H3VCQ=
|
||||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
@@ -39,10 +39,12 @@ github.com/cloudflare/cfssl v1.6.4/go.mod h1:8b3CQMxfWPAeom3zBnGJ6sd+G1NkL5TXqmD
|
|||||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||||
github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes=
|
github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao=
|
||||||
github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY=
|
github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4=
|
||||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
|
github.com/containerd/platforms v0.2.0 h1:clGNvVIcY3k39VJSYdFGohI1b3bP/eeBUVR5+XA28oo=
|
||||||
|
github.com/containerd/platforms v0.2.0/go.mod h1:XOM2BS6kN6gXafPLg80V6y/QUib+xoLyC3qVmHzibko=
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
@@ -57,8 +59,8 @@ github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5
|
|||||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v26.0.1-0.20240422144514-c8af8ebe4a89+incompatible h1:9eSHthT5Vvfh3aYy9xUaxMuecDicnMjfe7s9/qndRMw=
|
github.com/docker/docker v26.1.4-0.20240605103321-de5c9cf0b96e+incompatible h1:Zx3pZjoBZhEwxP38O4F6/NtfbhUIxzsz3H1N15dmZw8=
|
||||||
github.com/docker/docker v26.0.1-0.20240422144514-c8af8ebe4a89+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v26.1.4-0.20240605103321-de5c9cf0b96e+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
|
github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
|
||||||
github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||||
@@ -203,8 +205,8 @@ github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT
|
|||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
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/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=
|
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
|
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -327,8 +329,6 @@ golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
|||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
@@ -375,8 +375,6 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
|
|
||||||
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -421,5 +419,5 @@ gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
|||||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
|
k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
|
||||||
k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||||
tags.cncf.io/container-device-interface v0.6.2 h1:dThE6dtp/93ZDGhqaED2Pu374SOeUkBfuvkLuiTdwzg=
|
tags.cncf.io/container-device-interface v0.7.2 h1:MLqGnWfOr1wB7m08ieI4YJ3IoLKKozEnnNYBtacDPQU=
|
||||||
tags.cncf.io/container-device-interface v0.6.2/go.mod h1:Shusyhjs1A5Na/kqPVLL0KqnHQHuunol9LFeUNkuGVE=
|
tags.cncf.io/container-device-interface v0.7.2/go.mod h1:Xb1PvXv2BhfNb3tla4r9JL129ck1Lxv9KuU6eVOfKto=
|
||||||
|
|||||||
+4
-6
@@ -1,7 +1,3 @@
|
|||||||
run:
|
|
||||||
skip-dirs:
|
|
||||||
- pkg/etw/sample
|
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
# style
|
# style
|
||||||
@@ -20,9 +16,13 @@ linters:
|
|||||||
- gofmt # files are gofmt'ed
|
- gofmt # files are gofmt'ed
|
||||||
- gosec # security
|
- gosec # security
|
||||||
- nilerr # returns nil even with non-nil error
|
- nilerr # returns nil even with non-nil error
|
||||||
|
- thelper # test helpers without t.Helper()
|
||||||
- unparam # unused function params
|
- unparam # unused function params
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
|
exclude-dirs:
|
||||||
|
- pkg/etw/sample
|
||||||
|
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
# err is very often shadowed in nested scopes
|
# err is very often shadowed in nested scopes
|
||||||
- linters:
|
- linters:
|
||||||
@@ -69,9 +69,7 @@ linters-settings:
|
|||||||
# struct order is often for Win32 compat
|
# struct order is often for Win32 compat
|
||||||
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
|
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
|
||||||
- fieldalignment
|
- fieldalignment
|
||||||
check-shadowing: true
|
|
||||||
nolintlint:
|
nolintlint:
|
||||||
allow-leading-space: false
|
|
||||||
require-explanation: true
|
require-explanation: true
|
||||||
require-specific: true
|
require-specific: true
|
||||||
revive:
|
revive:
|
||||||
|
|||||||
+15
-18
@@ -10,14 +10,14 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/internal/fs"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
//sys backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||||
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
//sys backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BackupData = uint32(iota + 1)
|
BackupData = uint32(iota + 1)
|
||||||
@@ -104,7 +104,7 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
|||||||
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
hdr.Name = syscall.UTF16ToString(name)
|
hdr.Name = windows.UTF16ToString(name)
|
||||||
}
|
}
|
||||||
if wsi.StreamID == BackupSparseBlock {
|
if wsi.StreamID == BackupSparseBlock {
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||||
@@ -205,7 +205,7 @@ func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
|||||||
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||||
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||||
var bytesRead uint32
|
var bytesRead uint32
|
||||||
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
err := backupRead(windows.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
|
return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
@@ -220,7 +220,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) {
|
|||||||
// the underlying file.
|
// the underlying file.
|
||||||
func (r *BackupFileReader) Close() error {
|
func (r *BackupFileReader) Close() error {
|
||||||
if r.ctx != 0 {
|
if r.ctx != 0 {
|
||||||
_ = backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
_ = backupRead(windows.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||||
runtime.KeepAlive(r.f)
|
runtime.KeepAlive(r.f)
|
||||||
r.ctx = 0
|
r.ctx = 0
|
||||||
}
|
}
|
||||||
@@ -244,7 +244,7 @@ func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
|||||||
// Write restores a portion of the file using the provided backup stream.
|
// Write restores a portion of the file using the provided backup stream.
|
||||||
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||||
var bytesWritten uint32
|
var bytesWritten uint32
|
||||||
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
err := backupWrite(windows.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
|
return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
@@ -259,7 +259,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
|||||||
// close the underlying file.
|
// close the underlying file.
|
||||||
func (w *BackupFileWriter) Close() error {
|
func (w *BackupFileWriter) Close() error {
|
||||||
if w.ctx != 0 {
|
if w.ctx != 0 {
|
||||||
_ = backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
_ = backupWrite(windows.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||||
runtime.KeepAlive(w.f)
|
runtime.KeepAlive(w.f)
|
||||||
w.ctx = 0
|
w.ctx = 0
|
||||||
}
|
}
|
||||||
@@ -271,17 +271,14 @@ func (w *BackupFileWriter) Close() error {
|
|||||||
//
|
//
|
||||||
// If the file opened was a directory, it cannot be used with Readdir().
|
// If the file opened was a directory, it cannot be used with Readdir().
|
||||||
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||||
winPath, err := syscall.UTF16FromString(path)
|
h, err := fs.CreateFile(path,
|
||||||
if err != nil {
|
fs.AccessMask(access),
|
||||||
return nil, err
|
fs.FileShareMode(share),
|
||||||
}
|
|
||||||
h, err := syscall.CreateFile(&winPath[0],
|
|
||||||
access,
|
|
||||||
share,
|
|
||||||
nil,
|
nil,
|
||||||
createmode,
|
fs.FileCreationDisposition(createmode),
|
||||||
syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT,
|
fs.FILE_FLAG_BACKUP_SEMANTICS|fs.FILE_FLAG_OPEN_REPARSE_POINT,
|
||||||
0)
|
0,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = &os.PathError{Op: "open", Path: path, Err: err}
|
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
+37
-48
@@ -15,26 +15,11 @@ import (
|
|||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
//sys cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) = CancelIoEx
|
||||||
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
//sys createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) = CreateIoCompletionPort
|
||||||
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
//sys getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||||
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
//sys setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||||
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
//sys wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
||||||
|
|
||||||
type atomicBool int32
|
|
||||||
|
|
||||||
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
|
||||||
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
|
||||||
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
|
||||||
|
|
||||||
//revive:disable-next-line:predeclared Keep "new" to maintain consistency with "atomic" pkg
|
|
||||||
func (b *atomicBool) swap(new bool) bool {
|
|
||||||
var newInt int32
|
|
||||||
if new {
|
|
||||||
newInt = 1
|
|
||||||
}
|
|
||||||
return atomic.SwapInt32((*int32)(b), newInt) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrFileClosed = errors.New("file has already been closed")
|
ErrFileClosed = errors.New("file has already been closed")
|
||||||
@@ -50,7 +35,7 @@ func (*timeoutError) Temporary() bool { return true }
|
|||||||
type timeoutChan chan struct{}
|
type timeoutChan chan struct{}
|
||||||
|
|
||||||
var ioInitOnce sync.Once
|
var ioInitOnce sync.Once
|
||||||
var ioCompletionPort syscall.Handle
|
var ioCompletionPort windows.Handle
|
||||||
|
|
||||||
// ioResult contains the result of an asynchronous IO operation.
|
// ioResult contains the result of an asynchronous IO operation.
|
||||||
type ioResult struct {
|
type ioResult struct {
|
||||||
@@ -60,12 +45,12 @@ type ioResult struct {
|
|||||||
|
|
||||||
// ioOperation represents an outstanding asynchronous Win32 IO.
|
// ioOperation represents an outstanding asynchronous Win32 IO.
|
||||||
type ioOperation struct {
|
type ioOperation struct {
|
||||||
o syscall.Overlapped
|
o windows.Overlapped
|
||||||
ch chan ioResult
|
ch chan ioResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func initIO() {
|
func initIO() {
|
||||||
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
h, err := createIoCompletionPort(windows.InvalidHandle, 0, 0, 0xffffffff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -76,10 +61,10 @@ func initIO() {
|
|||||||
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||||
// It takes ownership of this handle and will close it if it is garbage collected.
|
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||||
type win32File struct {
|
type win32File struct {
|
||||||
handle syscall.Handle
|
handle windows.Handle
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
wgLock sync.RWMutex
|
wgLock sync.RWMutex
|
||||||
closing atomicBool
|
closing atomic.Bool
|
||||||
socket bool
|
socket bool
|
||||||
readDeadline deadlineHandler
|
readDeadline deadlineHandler
|
||||||
writeDeadline deadlineHandler
|
writeDeadline deadlineHandler
|
||||||
@@ -90,11 +75,11 @@ type deadlineHandler struct {
|
|||||||
channel timeoutChan
|
channel timeoutChan
|
||||||
channelLock sync.RWMutex
|
channelLock sync.RWMutex
|
||||||
timer *time.Timer
|
timer *time.Timer
|
||||||
timedout atomicBool
|
timedout atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeWin32File makes a new win32File from an existing file handle.
|
// makeWin32File makes a new win32File from an existing file handle.
|
||||||
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
func makeWin32File(h windows.Handle) (*win32File, error) {
|
||||||
f := &win32File{handle: h}
|
f := &win32File{handle: h}
|
||||||
ioInitOnce.Do(initIO)
|
ioInitOnce.Do(initIO)
|
||||||
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||||
@@ -110,7 +95,12 @@ func makeWin32File(h syscall.Handle) (*win32File, error) {
|
|||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: use NewOpenFile instead.
|
||||||
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
|
return NewOpenFile(windows.Handle(h))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOpenFile(h windows.Handle) (io.ReadWriteCloser, error) {
|
||||||
// If we return the result of makeWin32File directly, it can result in an
|
// If we return the result of makeWin32File directly, it can result in an
|
||||||
// interface-wrapped nil, rather than a nil interface value.
|
// interface-wrapped nil, rather than a nil interface value.
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
@@ -124,13 +114,13 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
|||||||
func (f *win32File) closeHandle() {
|
func (f *win32File) closeHandle() {
|
||||||
f.wgLock.Lock()
|
f.wgLock.Lock()
|
||||||
// Atomically set that we are closing, releasing the resources only once.
|
// Atomically set that we are closing, releasing the resources only once.
|
||||||
if !f.closing.swap(true) {
|
if !f.closing.Swap(true) {
|
||||||
f.wgLock.Unlock()
|
f.wgLock.Unlock()
|
||||||
// cancel all IO and wait for it to complete
|
// cancel all IO and wait for it to complete
|
||||||
_ = cancelIoEx(f.handle, nil)
|
_ = cancelIoEx(f.handle, nil)
|
||||||
f.wg.Wait()
|
f.wg.Wait()
|
||||||
// at this point, no new IO can start
|
// at this point, no new IO can start
|
||||||
syscall.Close(f.handle)
|
windows.Close(f.handle)
|
||||||
f.handle = 0
|
f.handle = 0
|
||||||
} else {
|
} else {
|
||||||
f.wgLock.Unlock()
|
f.wgLock.Unlock()
|
||||||
@@ -145,14 +135,14 @@ func (f *win32File) Close() error {
|
|||||||
|
|
||||||
// IsClosed checks if the file has been closed.
|
// IsClosed checks if the file has been closed.
|
||||||
func (f *win32File) IsClosed() bool {
|
func (f *win32File) IsClosed() bool {
|
||||||
return f.closing.isSet()
|
return f.closing.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareIO prepares for a new IO operation.
|
// prepareIO prepares for a new IO operation.
|
||||||
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
||||||
func (f *win32File) prepareIO() (*ioOperation, error) {
|
func (f *win32File) prepareIO() (*ioOperation, error) {
|
||||||
f.wgLock.RLock()
|
f.wgLock.RLock()
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
f.wgLock.RUnlock()
|
f.wgLock.RUnlock()
|
||||||
return nil, ErrFileClosed
|
return nil, ErrFileClosed
|
||||||
}
|
}
|
||||||
@@ -164,12 +154,12 @@ func (f *win32File) prepareIO() (*ioOperation, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ioCompletionProcessor processes completed async IOs forever.
|
// ioCompletionProcessor processes completed async IOs forever.
|
||||||
func ioCompletionProcessor(h syscall.Handle) {
|
func ioCompletionProcessor(h windows.Handle) {
|
||||||
for {
|
for {
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
var key uintptr
|
var key uintptr
|
||||||
var op *ioOperation
|
var op *ioOperation
|
||||||
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
err := getQueuedCompletionStatus(h, &bytes, &key, &op, windows.INFINITE)
|
||||||
if op == nil {
|
if op == nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -182,11 +172,11 @@ func ioCompletionProcessor(h syscall.Handle) {
|
|||||||
// asyncIO processes the return value from ReadFile or WriteFile, blocking until
|
// asyncIO processes the return value from ReadFile or WriteFile, blocking until
|
||||||
// the operation has actually completed.
|
// the operation has actually completed.
|
||||||
func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
||||||
if err != syscall.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
|
if err != windows.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
|
||||||
return int(bytes), err
|
return int(bytes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
_ = cancelIoEx(f.handle, &c.o)
|
_ = cancelIoEx(f.handle, &c.o)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,8 +191,8 @@ func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
|||||||
select {
|
select {
|
||||||
case r = <-c.ch:
|
case r = <-c.ch:
|
||||||
err = r.err
|
err = r.err
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
err = ErrFileClosed
|
err = ErrFileClosed
|
||||||
}
|
}
|
||||||
} else if err != nil && f.socket {
|
} else if err != nil && f.socket {
|
||||||
@@ -214,7 +204,7 @@ func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
|||||||
_ = cancelIoEx(f.handle, &c.o)
|
_ = cancelIoEx(f.handle, &c.o)
|
||||||
r = <-c.ch
|
r = <-c.ch
|
||||||
err = r.err
|
err = r.err
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
||||||
err = ErrTimeout
|
err = ErrTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,23 +225,22 @@ func (f *win32File) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
defer f.wg.Done()
|
defer f.wg.Done()
|
||||||
|
|
||||||
if f.readDeadline.timedout.isSet() {
|
if f.readDeadline.timedout.Load() {
|
||||||
return 0, ErrTimeout
|
return 0, ErrTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
err = windows.ReadFile(f.handle, b, &bytes, &c.o)
|
||||||
n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
|
n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
|
||||||
runtime.KeepAlive(b)
|
runtime.KeepAlive(b)
|
||||||
|
|
||||||
// Handle EOF conditions.
|
// Handle EOF conditions.
|
||||||
if err == nil && n == 0 && len(b) != 0 {
|
if err == nil && n == 0 && len(b) != 0 {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
} else if err == syscall.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
|
} else if err == windows.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
} else {
|
|
||||||
return n, err
|
|
||||||
}
|
}
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes to a file handle.
|
// Write writes to a file handle.
|
||||||
@@ -262,12 +251,12 @@ func (f *win32File) Write(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
defer f.wg.Done()
|
defer f.wg.Done()
|
||||||
|
|
||||||
if f.writeDeadline.timedout.isSet() {
|
if f.writeDeadline.timedout.Load() {
|
||||||
return 0, ErrTimeout
|
return 0, ErrTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
err = windows.WriteFile(f.handle, b, &bytes, &c.o)
|
||||||
n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
|
n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
|
||||||
runtime.KeepAlive(b)
|
runtime.KeepAlive(b)
|
||||||
return n, err
|
return n, err
|
||||||
@@ -282,7 +271,7 @@ func (f *win32File) SetWriteDeadline(deadline time.Time) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *win32File) Flush() error {
|
func (f *win32File) Flush() error {
|
||||||
return syscall.FlushFileBuffers(f.handle)
|
return windows.FlushFileBuffers(f.handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *win32File) Fd() uintptr {
|
func (f *win32File) Fd() uintptr {
|
||||||
@@ -299,7 +288,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
|
|||||||
}
|
}
|
||||||
d.timer = nil
|
d.timer = nil
|
||||||
}
|
}
|
||||||
d.timedout.setFalse()
|
d.timedout.Store(false)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-d.channel:
|
case <-d.channel:
|
||||||
@@ -314,7 +303,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeoutIO := func() {
|
timeoutIO := func() {
|
||||||
d.timedout.setTrue()
|
d.timedout.Store(true)
|
||||||
close(d.channel)
|
close(d.channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-4
@@ -18,9 +18,18 @@ type FileBasicInfo struct {
|
|||||||
_ uint32 // padding
|
_ uint32 // padding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// alignedFileBasicInfo is a FileBasicInfo, but aligned to uint64 by containing
|
||||||
|
// uint64 rather than windows.Filetime. Filetime contains two uint32s. uint64
|
||||||
|
// alignment is necessary to pass this as FILE_BASIC_INFO.
|
||||||
|
type alignedFileBasicInfo struct {
|
||||||
|
CreationTime, LastAccessTime, LastWriteTime, ChangeTime uint64
|
||||||
|
FileAttributes uint32
|
||||||
|
_ uint32 // padding
|
||||||
|
}
|
||||||
|
|
||||||
// GetFileBasicInfo retrieves times and attributes for a file.
|
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||||
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||||
bi := &FileBasicInfo{}
|
bi := &alignedFileBasicInfo{}
|
||||||
if err := windows.GetFileInformationByHandleEx(
|
if err := windows.GetFileInformationByHandleEx(
|
||||||
windows.Handle(f.Fd()),
|
windows.Handle(f.Fd()),
|
||||||
windows.FileBasicInfo,
|
windows.FileBasicInfo,
|
||||||
@@ -30,16 +39,21 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
|||||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
runtime.KeepAlive(f)
|
runtime.KeepAlive(f)
|
||||||
return bi, nil
|
// Reinterpret the alignedFileBasicInfo as a FileBasicInfo so it matches the
|
||||||
|
// public API of this module. The data may be unnecessarily aligned.
|
||||||
|
return (*FileBasicInfo)(unsafe.Pointer(bi)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFileBasicInfo sets times and attributes for a file.
|
// SetFileBasicInfo sets times and attributes for a file.
|
||||||
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||||
|
// Create an alignedFileBasicInfo based on a FileBasicInfo. The copy is
|
||||||
|
// suitable to pass to GetFileInformationByHandleEx.
|
||||||
|
biAligned := *(*alignedFileBasicInfo)(unsafe.Pointer(bi))
|
||||||
if err := windows.SetFileInformationByHandle(
|
if err := windows.SetFileInformationByHandle(
|
||||||
windows.Handle(f.Fd()),
|
windows.Handle(f.Fd()),
|
||||||
windows.FileBasicInfo,
|
windows.FileBasicInfo,
|
||||||
(*byte)(unsafe.Pointer(bi)),
|
(*byte)(unsafe.Pointer(&biAligned)),
|
||||||
uint32(unsafe.Sizeof(*bi)),
|
uint32(unsafe.Sizeof(biAligned)),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-20
@@ -10,7 +10,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@@ -181,13 +180,13 @@ type HvsockConn struct {
|
|||||||
var _ net.Conn = &HvsockConn{}
|
var _ net.Conn = &HvsockConn{}
|
||||||
|
|
||||||
func newHVSocket() (*win32File, error) {
|
func newHVSocket() (*win32File, error) {
|
||||||
fd, err := syscall.Socket(afHVSock, syscall.SOCK_STREAM, 1)
|
fd, err := windows.Socket(afHVSock, windows.SOCK_STREAM, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, os.NewSyscallError("socket", err)
|
return nil, os.NewSyscallError("socket", err)
|
||||||
}
|
}
|
||||||
f, err := makeWin32File(fd)
|
f, err := makeWin32File(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(fd)
|
windows.Close(fd)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
f.socket = true
|
f.socket = true
|
||||||
@@ -197,16 +196,24 @@ func newHVSocket() (*win32File, error) {
|
|||||||
// ListenHvsock listens for connections on the specified hvsock address.
|
// ListenHvsock listens for connections on the specified hvsock address.
|
||||||
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
||||||
l := &HvsockListener{addr: *addr}
|
l := &HvsockListener{addr: *addr}
|
||||||
sock, err := newHVSocket()
|
|
||||||
|
var sock *win32File
|
||||||
|
sock, err = newHVSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", err)
|
return nil, l.opErr("listen", err)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = sock.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
sa := addr.raw()
|
sa := addr.raw()
|
||||||
err = socket.Bind(windows.Handle(sock.handle), &sa)
|
err = socket.Bind(sock.handle, &sa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
||||||
}
|
}
|
||||||
err = syscall.Listen(sock.handle, 16)
|
err = windows.Listen(sock.handle, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
||||||
}
|
}
|
||||||
@@ -246,7 +253,7 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
|||||||
var addrbuf [addrlen * 2]byte
|
var addrbuf [addrlen * 2]byte
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
|
err = windows.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
|
||||||
if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
|
if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
|
||||||
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
||||||
}
|
}
|
||||||
@@ -263,7 +270,7 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
|||||||
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
||||||
|
|
||||||
// initialize the accepted socket and update its properties with those of the listening socket
|
// initialize the accepted socket and update its properties with those of the listening socket
|
||||||
if err = windows.Setsockopt(windows.Handle(sock.handle),
|
if err = windows.Setsockopt(sock.handle,
|
||||||
windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
|
windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
|
||||||
(*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
|
(*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
|
||||||
return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
|
return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
|
||||||
@@ -334,7 +341,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
sa := addr.raw()
|
sa := addr.raw()
|
||||||
err = socket.Bind(windows.Handle(sock.handle), &sa)
|
err = socket.Bind(sock.handle, &sa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, conn.opErr(op, os.NewSyscallError("bind", err))
|
return nil, conn.opErr(op, os.NewSyscallError("bind", err))
|
||||||
}
|
}
|
||||||
@@ -347,7 +354,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
|||||||
var bytes uint32
|
var bytes uint32
|
||||||
for i := uint(0); i <= d.Retries; i++ {
|
for i := uint(0); i <= d.Retries; i++ {
|
||||||
err = socket.ConnectEx(
|
err = socket.ConnectEx(
|
||||||
windows.Handle(sock.handle),
|
sock.handle,
|
||||||
&sa,
|
&sa,
|
||||||
nil, // sendBuf
|
nil, // sendBuf
|
||||||
0, // sendDataLen
|
0, // sendDataLen
|
||||||
@@ -367,7 +374,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
|||||||
|
|
||||||
// update the connection properties, so shutdown can be used
|
// update the connection properties, so shutdown can be used
|
||||||
if err = windows.Setsockopt(
|
if err = windows.Setsockopt(
|
||||||
windows.Handle(sock.handle),
|
sock.handle,
|
||||||
windows.SOL_SOCKET,
|
windows.SOL_SOCKET,
|
||||||
windows.SO_UPDATE_CONNECT_CONTEXT,
|
windows.SO_UPDATE_CONNECT_CONTEXT,
|
||||||
nil, // optvalue
|
nil, // optvalue
|
||||||
@@ -378,7 +385,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
|||||||
|
|
||||||
// get the local name
|
// get the local name
|
||||||
var sal rawHvsockAddr
|
var sal rawHvsockAddr
|
||||||
err = socket.GetSockName(windows.Handle(sock.handle), &sal)
|
err = socket.GetSockName(sock.handle, &sal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
|
return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
|
||||||
}
|
}
|
||||||
@@ -421,7 +428,7 @@ func (d *HvsockDialer) redialWait(ctx context.Context) (err error) {
|
|||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// assumes error is a plain, unwrapped syscall.Errno provided by direct syscall.
|
// assumes error is a plain, unwrapped windows.Errno provided by direct syscall.
|
||||||
func canRedial(err error) bool {
|
func canRedial(err error) bool {
|
||||||
//nolint:errorlint // guaranteed to be an Errno
|
//nolint:errorlint // guaranteed to be an Errno
|
||||||
switch err {
|
switch err {
|
||||||
@@ -447,9 +454,9 @@ func (conn *HvsockConn) Read(b []byte) (int, error) {
|
|||||||
return 0, conn.opErr("read", err)
|
return 0, conn.opErr("read", err)
|
||||||
}
|
}
|
||||||
defer conn.sock.wg.Done()
|
defer conn.sock.wg.Done()
|
||||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||||
var flags, bytes uint32
|
var flags, bytes uint32
|
||||||
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
err = windows.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
||||||
n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
|
n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var eno windows.Errno
|
var eno windows.Errno
|
||||||
@@ -482,9 +489,9 @@ func (conn *HvsockConn) write(b []byte) (int, error) {
|
|||||||
return 0, conn.opErr("write", err)
|
return 0, conn.opErr("write", err)
|
||||||
}
|
}
|
||||||
defer conn.sock.wg.Done()
|
defer conn.sock.wg.Done()
|
||||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
err = windows.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
||||||
n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
|
n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var eno windows.Errno
|
var eno windows.Errno
|
||||||
@@ -511,7 +518,7 @@ func (conn *HvsockConn) shutdown(how int) error {
|
|||||||
return socket.ErrSocketClosed
|
return socket.ErrSocketClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
err := syscall.Shutdown(conn.sock.handle, how)
|
err := windows.Shutdown(conn.sock.handle, how)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the connection was closed, shutdowns fail with "not connected"
|
// If the connection was closed, shutdowns fail with "not connected"
|
||||||
if errors.Is(err, windows.WSAENOTCONN) ||
|
if errors.Is(err, windows.WSAENOTCONN) ||
|
||||||
@@ -525,7 +532,7 @@ func (conn *HvsockConn) shutdown(how int) error {
|
|||||||
|
|
||||||
// CloseRead shuts down the read end of the socket, preventing future read operations.
|
// CloseRead shuts down the read end of the socket, preventing future read operations.
|
||||||
func (conn *HvsockConn) CloseRead() error {
|
func (conn *HvsockConn) CloseRead() error {
|
||||||
err := conn.shutdown(syscall.SHUT_RD)
|
err := conn.shutdown(windows.SHUT_RD)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return conn.opErr("closeread", err)
|
return conn.opErr("closeread", err)
|
||||||
}
|
}
|
||||||
@@ -535,7 +542,7 @@ func (conn *HvsockConn) CloseRead() error {
|
|||||||
// CloseWrite shuts down the write end of the socket, preventing future write operations and
|
// CloseWrite shuts down the write end of the socket, preventing future write operations and
|
||||||
// notifying the other endpoint that no more data will be written.
|
// notifying the other endpoint that no more data will be written.
|
||||||
func (conn *HvsockConn) CloseWrite() error {
|
func (conn *HvsockConn) CloseWrite() error {
|
||||||
err := conn.shutdown(syscall.SHUT_WR)
|
err := conn.shutdown(windows.SHUT_WR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return conn.opErr("closewrite", err)
|
return conn.opErr("closewrite", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-6
@@ -11,12 +11,14 @@ import (
|
|||||||
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
|
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
||||||
//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
|
//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
|
||||||
|
|
||||||
const NullHandle windows.Handle = 0
|
const NullHandle windows.Handle = 0
|
||||||
|
|
||||||
// AccessMask defines standard, specific, and generic rights.
|
// AccessMask defines standard, specific, and generic rights.
|
||||||
//
|
//
|
||||||
|
// Used with CreateFile and NtCreateFile (and co.).
|
||||||
|
//
|
||||||
// Bitmask:
|
// Bitmask:
|
||||||
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
|
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
|
||||||
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||||
@@ -47,6 +49,12 @@ const (
|
|||||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
|
||||||
FILE_ANY_ACCESS AccessMask = 0
|
FILE_ANY_ACCESS AccessMask = 0
|
||||||
|
|
||||||
|
GENERIC_READ AccessMask = 0x8000_0000
|
||||||
|
GENERIC_WRITE AccessMask = 0x4000_0000
|
||||||
|
GENERIC_EXECUTE AccessMask = 0x2000_0000
|
||||||
|
GENERIC_ALL AccessMask = 0x1000_0000
|
||||||
|
ACCESS_SYSTEM_SECURITY AccessMask = 0x0100_0000
|
||||||
|
|
||||||
// Specific Object Access
|
// Specific Object Access
|
||||||
// from ntioapi.h
|
// from ntioapi.h
|
||||||
|
|
||||||
@@ -124,14 +132,32 @@ const (
|
|||||||
TRUNCATE_EXISTING FileCreationDisposition = 0x05
|
TRUNCATE_EXISTING FileCreationDisposition = 0x05
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Create disposition values for NtCreate*
|
||||||
|
type NTFileCreationDisposition uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// From ntioapi.h
|
||||||
|
|
||||||
|
FILE_SUPERSEDE NTFileCreationDisposition = 0x00
|
||||||
|
FILE_OPEN NTFileCreationDisposition = 0x01
|
||||||
|
FILE_CREATE NTFileCreationDisposition = 0x02
|
||||||
|
FILE_OPEN_IF NTFileCreationDisposition = 0x03
|
||||||
|
FILE_OVERWRITE NTFileCreationDisposition = 0x04
|
||||||
|
FILE_OVERWRITE_IF NTFileCreationDisposition = 0x05
|
||||||
|
FILE_MAXIMUM_DISPOSITION NTFileCreationDisposition = 0x05
|
||||||
|
)
|
||||||
|
|
||||||
// CreateFile and co. take flags or attributes together as one parameter.
|
// CreateFile and co. take flags or attributes together as one parameter.
|
||||||
// Define alias until we can use generics to allow both
|
// Define alias until we can use generics to allow both
|
||||||
|
//
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
||||||
type FileFlagOrAttribute uint32
|
type FileFlagOrAttribute uint32
|
||||||
|
|
||||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
const ( // from winnt.h
|
const (
|
||||||
|
// from winnt.h
|
||||||
|
|
||||||
FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
|
FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
|
||||||
FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
|
FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
|
||||||
FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
|
FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
|
||||||
@@ -145,17 +171,51 @@ const ( // from winnt.h
|
|||||||
FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
|
FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NtCreate* functions take a dedicated CreateOptions parameter.
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/Winternl/nf-winternl-ntcreatefile
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/devnotes/nt-create-named-pipe-file
|
||||||
|
type NTCreateOptions uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// From ntioapi.h
|
||||||
|
|
||||||
|
FILE_DIRECTORY_FILE NTCreateOptions = 0x0000_0001
|
||||||
|
FILE_WRITE_THROUGH NTCreateOptions = 0x0000_0002
|
||||||
|
FILE_SEQUENTIAL_ONLY NTCreateOptions = 0x0000_0004
|
||||||
|
FILE_NO_INTERMEDIATE_BUFFERING NTCreateOptions = 0x0000_0008
|
||||||
|
|
||||||
|
FILE_SYNCHRONOUS_IO_ALERT NTCreateOptions = 0x0000_0010
|
||||||
|
FILE_SYNCHRONOUS_IO_NONALERT NTCreateOptions = 0x0000_0020
|
||||||
|
FILE_NON_DIRECTORY_FILE NTCreateOptions = 0x0000_0040
|
||||||
|
FILE_CREATE_TREE_CONNECTION NTCreateOptions = 0x0000_0080
|
||||||
|
|
||||||
|
FILE_COMPLETE_IF_OPLOCKED NTCreateOptions = 0x0000_0100
|
||||||
|
FILE_NO_EA_KNOWLEDGE NTCreateOptions = 0x0000_0200
|
||||||
|
FILE_DISABLE_TUNNELING NTCreateOptions = 0x0000_0400
|
||||||
|
FILE_RANDOM_ACCESS NTCreateOptions = 0x0000_0800
|
||||||
|
|
||||||
|
FILE_DELETE_ON_CLOSE NTCreateOptions = 0x0000_1000
|
||||||
|
FILE_OPEN_BY_FILE_ID NTCreateOptions = 0x0000_2000
|
||||||
|
FILE_OPEN_FOR_BACKUP_INTENT NTCreateOptions = 0x0000_4000
|
||||||
|
FILE_NO_COMPRESSION NTCreateOptions = 0x0000_8000
|
||||||
|
)
|
||||||
|
|
||||||
type FileSQSFlag = FileFlagOrAttribute
|
type FileSQSFlag = FileFlagOrAttribute
|
||||||
|
|
||||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
const ( // from winbase.h
|
const (
|
||||||
|
// from winbase.h
|
||||||
|
|
||||||
SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
|
SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
|
||||||
SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
|
SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
|
||||||
SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
|
SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
|
||||||
SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
|
SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
|
||||||
|
|
||||||
SECURITY_SQOS_PRESENT FileSQSFlag = 0x00100000
|
SECURITY_SQOS_PRESENT FileSQSFlag = 0x0010_0000
|
||||||
SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F0000
|
SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F_0000
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetFinalPathNameByHandle flags
|
// GetFinalPathNameByHandle flags
|
||||||
|
|||||||
+3
-6
@@ -33,9 +33,6 @@ func errnoErr(e syscall.Errno) error {
|
|||||||
case errnoERROR_IO_PENDING:
|
case errnoERROR_IO_PENDING:
|
||||||
return errERROR_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
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +42,7 @@ var (
|
|||||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
func CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
||||||
var _p0 *uint16
|
var _p0 *uint16
|
||||||
_p0, err = syscall.UTF16PtrFromString(name)
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -54,8 +51,8 @@ func CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.
|
|||||||
return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.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)
|
r0, _, e1 := syscall.SyscallN(procCreateFileW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile))
|
||||||
handle = windows.Handle(r0)
|
handle = windows.Handle(r0)
|
||||||
if handle == windows.InvalidHandle {
|
if handle == windows.InvalidHandle {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
|
|||||||
+3
-5
@@ -156,9 +156,7 @@ func connectEx(
|
|||||||
bytesSent *uint32,
|
bytesSent *uint32,
|
||||||
overlapped *windows.Overlapped,
|
overlapped *windows.Overlapped,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
// todo: after upgrading to 1.18, switch from syscall.Syscall9 to syscall.SyscallN
|
r1, _, e1 := syscall.SyscallN(connectExFunc.addr,
|
||||||
r1, _, e1 := syscall.Syscall9(connectExFunc.addr,
|
|
||||||
7,
|
|
||||||
uintptr(s),
|
uintptr(s),
|
||||||
uintptr(name),
|
uintptr(name),
|
||||||
uintptr(namelen),
|
uintptr(namelen),
|
||||||
@@ -166,8 +164,8 @@ func connectEx(
|
|||||||
uintptr(sendDataLen),
|
uintptr(sendDataLen),
|
||||||
uintptr(unsafe.Pointer(bytesSent)),
|
uintptr(unsafe.Pointer(bytesSent)),
|
||||||
uintptr(unsafe.Pointer(overlapped)),
|
uintptr(unsafe.Pointer(overlapped)),
|
||||||
0,
|
)
|
||||||
0)
|
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = error(e1)
|
err = error(e1)
|
||||||
|
|||||||
+3
-6
@@ -33,9 +33,6 @@ func errnoErr(e syscall.Errno) error {
|
|||||||
case errnoERROR_IO_PENDING:
|
case errnoERROR_IO_PENDING:
|
||||||
return errERROR_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
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +45,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
|
r1, _, e1 := syscall.SyscallN(procbind.Addr(), uintptr(s), uintptr(name), uintptr(namelen))
|
||||||
if r1 == socketError {
|
if r1 == socketError {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -56,7 +53,7 @@ func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
r1, _, e1 := syscall.SyscallN(procgetpeername.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
||||||
if r1 == socketError {
|
if r1 == socketError {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -64,7 +61,7 @@ func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
r1, _, e1 := syscall.SyscallN(procgetsockname.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
||||||
if r1 == socketError {
|
if r1 == socketError {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -62,7 +62,7 @@ func (b *WString) Free() {
|
|||||||
// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
|
// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
|
||||||
// previous buffer back into pool.
|
// previous buffer back into pool.
|
||||||
func (b *WString) ResizeTo(c uint32) uint32 {
|
func (b *WString) ResizeTo(c uint32) uint32 {
|
||||||
// allready sufficient (or n is 0)
|
// already sufficient (or n is 0)
|
||||||
if c <= b.Cap() {
|
if c <= b.Cap() {
|
||||||
return b.Cap()
|
return b.Cap()
|
||||||
}
|
}
|
||||||
|
|||||||
+93
-32
@@ -11,7 +11,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@@ -20,20 +19,44 @@ import (
|
|||||||
"github.com/Microsoft/go-winio/internal/fs"
|
"github.com/Microsoft/go-winio/internal/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
//sys connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) = ConnectNamedPipe
|
||||||
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateNamedPipeW
|
||||||
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
//sys disconnectNamedPipe(pipe windows.Handle) (err error) = DisconnectNamedPipe
|
||||||
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
//sys getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||||
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
|
//sys getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||||
//sys 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) = ntdll.NtCreateNamedPipeFile
|
//sys ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
|
||||||
//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
||||||
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
|
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
|
||||||
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
|
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
|
||||||
|
|
||||||
|
type PipeConn interface {
|
||||||
|
net.Conn
|
||||||
|
Disconnect() error
|
||||||
|
Flush() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// type aliases for mkwinsyscall code
|
||||||
|
type (
|
||||||
|
ntAccessMask = fs.AccessMask
|
||||||
|
ntFileShareMode = fs.FileShareMode
|
||||||
|
ntFileCreationDisposition = fs.NTFileCreationDisposition
|
||||||
|
ntFileOptions = fs.NTCreateOptions
|
||||||
|
)
|
||||||
|
|
||||||
type ioStatusBlock struct {
|
type ioStatusBlock struct {
|
||||||
Status, Information uintptr
|
Status, Information uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// typedef struct _OBJECT_ATTRIBUTES {
|
||||||
|
// ULONG Length;
|
||||||
|
// HANDLE RootDirectory;
|
||||||
|
// PUNICODE_STRING ObjectName;
|
||||||
|
// ULONG Attributes;
|
||||||
|
// PVOID SecurityDescriptor;
|
||||||
|
// PVOID SecurityQualityOfService;
|
||||||
|
// } OBJECT_ATTRIBUTES;
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes
|
||||||
type objectAttributes struct {
|
type objectAttributes struct {
|
||||||
Length uintptr
|
Length uintptr
|
||||||
RootDirectory uintptr
|
RootDirectory uintptr
|
||||||
@@ -49,6 +72,17 @@ type unicodeString struct {
|
|||||||
Buffer uintptr
|
Buffer uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// typedef struct _SECURITY_DESCRIPTOR {
|
||||||
|
// BYTE Revision;
|
||||||
|
// BYTE Sbz1;
|
||||||
|
// SECURITY_DESCRIPTOR_CONTROL Control;
|
||||||
|
// PSID Owner;
|
||||||
|
// PSID Group;
|
||||||
|
// PACL Sacl;
|
||||||
|
// PACL Dacl;
|
||||||
|
// } SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_descriptor
|
||||||
type securityDescriptor struct {
|
type securityDescriptor struct {
|
||||||
Revision byte
|
Revision byte
|
||||||
Sbz1 byte
|
Sbz1 byte
|
||||||
@@ -80,6 +114,8 @@ type win32Pipe struct {
|
|||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ PipeConn = (*win32Pipe)(nil)
|
||||||
|
|
||||||
type win32MessageBytePipe struct {
|
type win32MessageBytePipe struct {
|
||||||
win32Pipe
|
win32Pipe
|
||||||
writeClosed bool
|
writeClosed bool
|
||||||
@@ -103,6 +139,10 @@ func (f *win32Pipe) SetDeadline(t time.Time) error {
|
|||||||
return f.SetWriteDeadline(t)
|
return f.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) Disconnect() error {
|
||||||
|
return disconnectNamedPipe(f.win32File.handle)
|
||||||
|
}
|
||||||
|
|
||||||
// CloseWrite closes the write side of a message pipe in byte mode.
|
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||||
func (f *win32MessageBytePipe) CloseWrite() error {
|
func (f *win32MessageBytePipe) CloseWrite() error {
|
||||||
if f.writeClosed {
|
if f.writeClosed {
|
||||||
@@ -146,7 +186,7 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
|||||||
// zero-byte message, ensure that all future Read() calls
|
// zero-byte message, ensure that all future Read() calls
|
||||||
// also return EOF.
|
// also return EOF.
|
||||||
f.readEOF = true
|
f.readEOF = true
|
||||||
} else if err == syscall.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
|
} else if err == windows.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
|
||||||
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
||||||
// and the message still has more bytes. Treat this as a success, since
|
// and the message still has more bytes. Treat this as a success, since
|
||||||
// this package presents all named pipes as byte streams.
|
// this package presents all named pipes as byte streams.
|
||||||
@@ -164,21 +204,20 @@ func (s pipeAddress) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
||||||
func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask) (syscall.Handle, error) {
|
func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask, impLevel PipeImpLevel) (windows.Handle, error) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return syscall.Handle(0), ctx.Err()
|
return windows.Handle(0), ctx.Err()
|
||||||
default:
|
default:
|
||||||
wh, err := fs.CreateFile(*path,
|
h, err := fs.CreateFile(*path,
|
||||||
access,
|
access,
|
||||||
0, // mode
|
0, // mode
|
||||||
nil, // security attributes
|
nil, // security attributes
|
||||||
fs.OPEN_EXISTING,
|
fs.OPEN_EXISTING,
|
||||||
fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.SECURITY_ANONYMOUS,
|
fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.FileSQSFlag(impLevel),
|
||||||
0, // template file handle
|
0, // template file handle
|
||||||
)
|
)
|
||||||
h := syscall.Handle(wh)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
@@ -214,15 +253,33 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
|||||||
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
||||||
// cancellation or timeout.
|
// cancellation or timeout.
|
||||||
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
||||||
return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE)
|
return DialPipeAccess(ctx, path, uint32(fs.GENERIC_READ|fs.GENERIC_WRITE))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PipeImpLevel is an enumeration of impersonation levels that may be set
|
||||||
|
// when calling DialPipeAccessImpersonation.
|
||||||
|
type PipeImpLevel uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PipeImpLevelAnonymous = PipeImpLevel(fs.SECURITY_ANONYMOUS)
|
||||||
|
PipeImpLevelIdentification = PipeImpLevel(fs.SECURITY_IDENTIFICATION)
|
||||||
|
PipeImpLevelImpersonation = PipeImpLevel(fs.SECURITY_IMPERSONATION)
|
||||||
|
PipeImpLevelDelegation = PipeImpLevel(fs.SECURITY_DELEGATION)
|
||||||
|
)
|
||||||
|
|
||||||
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
|
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
|
||||||
// cancellation or timeout.
|
// cancellation or timeout.
|
||||||
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
|
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
|
||||||
|
return DialPipeAccessImpLevel(ctx, path, access, PipeImpLevelAnonymous)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialPipeAccessImpLevel attempts to connect to a named pipe by `path` with
|
||||||
|
// `access` at `impLevel` until `ctx` cancellation or timeout. The other
|
||||||
|
// DialPipe* implementations use PipeImpLevelAnonymous.
|
||||||
|
func DialPipeAccessImpLevel(ctx context.Context, path string, access uint32, impLevel PipeImpLevel) (net.Conn, error) {
|
||||||
var err error
|
var err error
|
||||||
var h syscall.Handle
|
var h windows.Handle
|
||||||
h, err = tryDialPipe(ctx, &path, fs.AccessMask(access))
|
h, err = tryDialPipe(ctx, &path, fs.AccessMask(access), impLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -235,7 +292,7 @@ func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn,
|
|||||||
|
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(h)
|
windows.Close(h)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +312,7 @@ type acceptResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type win32PipeListener struct {
|
type win32PipeListener struct {
|
||||||
firstHandle syscall.Handle
|
firstHandle windows.Handle
|
||||||
path string
|
path string
|
||||||
config PipeConfig
|
config PipeConfig
|
||||||
acceptCh chan (chan acceptResponse)
|
acceptCh chan (chan acceptResponse)
|
||||||
@@ -263,8 +320,8 @@ type win32PipeListener struct {
|
|||||||
doneCh chan int
|
doneCh chan int
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
|
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (windows.Handle, error) {
|
||||||
path16, err := syscall.UTF16FromString(path)
|
path16, err := windows.UTF16FromString(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
}
|
}
|
||||||
@@ -280,16 +337,20 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
|||||||
).Err(); err != nil {
|
).Err(); err != nil {
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
}
|
}
|
||||||
defer localFree(ntPath.Buffer)
|
defer windows.LocalFree(windows.Handle(ntPath.Buffer)) //nolint:errcheck
|
||||||
oa.ObjectName = &ntPath
|
oa.ObjectName = &ntPath
|
||||||
oa.Attributes = windows.OBJ_CASE_INSENSITIVE
|
oa.Attributes = windows.OBJ_CASE_INSENSITIVE
|
||||||
|
|
||||||
// The security descriptor is only needed for the first pipe.
|
// The security descriptor is only needed for the first pipe.
|
||||||
if first {
|
if first {
|
||||||
if sd != nil {
|
if sd != nil {
|
||||||
|
//todo: does `sdb` need to be allocated on the heap, or can go allocate it?
|
||||||
l := uint32(len(sd))
|
l := uint32(len(sd))
|
||||||
sdb := localAlloc(0, l)
|
sdb, err := windows.LocalAlloc(0, l)
|
||||||
defer localFree(sdb)
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("LocalAlloc for security descriptor with of length %d: %w", l, err)
|
||||||
|
}
|
||||||
|
defer windows.LocalFree(windows.Handle(sdb)) //nolint:errcheck
|
||||||
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
|
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
|
||||||
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
|
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
|
||||||
} else {
|
} else {
|
||||||
@@ -298,7 +359,7 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
|||||||
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
|
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
|
||||||
return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
|
return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
|
||||||
}
|
}
|
||||||
defer localFree(dacl)
|
defer windows.LocalFree(windows.Handle(dacl)) //nolint:errcheck
|
||||||
|
|
||||||
sdb := &securityDescriptor{
|
sdb := &securityDescriptor{
|
||||||
Revision: 1,
|
Revision: 1,
|
||||||
@@ -314,27 +375,27 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
|||||||
typ |= windows.FILE_PIPE_MESSAGE_TYPE
|
typ |= windows.FILE_PIPE_MESSAGE_TYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
disposition := uint32(windows.FILE_OPEN)
|
disposition := fs.FILE_OPEN
|
||||||
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
|
access := fs.GENERIC_READ | fs.GENERIC_WRITE | fs.SYNCHRONIZE
|
||||||
if first {
|
if first {
|
||||||
disposition = windows.FILE_CREATE
|
disposition = fs.FILE_CREATE
|
||||||
// By not asking for read or write access, the named pipe file system
|
// By not asking for read or write access, the named pipe file system
|
||||||
// will put this pipe into an initially disconnected state, blocking
|
// will put this pipe into an initially disconnected state, blocking
|
||||||
// client connections until the next call with first == false.
|
// client connections until the next call with first == false.
|
||||||
access = syscall.SYNCHRONIZE
|
access = fs.SYNCHRONIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout := int64(-50 * 10000) // 50ms
|
timeout := int64(-50 * 10000) // 50ms
|
||||||
|
|
||||||
var (
|
var (
|
||||||
h syscall.Handle
|
h windows.Handle
|
||||||
iosb ioStatusBlock
|
iosb ioStatusBlock
|
||||||
)
|
)
|
||||||
err = ntCreateNamedPipeFile(&h,
|
err = ntCreateNamedPipeFile(&h,
|
||||||
access,
|
access,
|
||||||
&oa,
|
&oa,
|
||||||
&iosb,
|
&iosb,
|
||||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE,
|
fs.FILE_SHARE_READ|fs.FILE_SHARE_WRITE,
|
||||||
disposition,
|
disposition,
|
||||||
0,
|
0,
|
||||||
typ,
|
typ,
|
||||||
@@ -359,7 +420,7 @@ func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
|||||||
}
|
}
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(h)
|
windows.Close(h)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return f, nil
|
return f, nil
|
||||||
@@ -418,7 +479,7 @@ func (l *win32PipeListener) listenerRoutine() {
|
|||||||
closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
|
closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
syscall.Close(l.firstHandle)
|
windows.Close(l.firstHandle)
|
||||||
l.firstHandle = 0
|
l.firstHandle = 0
|
||||||
// Notify Close() and Accept() callers that the handle has been closed.
|
// Notify Close() and Accept() callers that the handle has been closed.
|
||||||
close(l.doneCh)
|
close(l.doneCh)
|
||||||
|
|||||||
+4
-5
@@ -9,7 +9,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@@ -18,8 +17,8 @@ import (
|
|||||||
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||||
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||||
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||||
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
//sys openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
||||||
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
//sys getCurrentThread() (h windows.Handle) = GetCurrentThread
|
||||||
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||||
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||||
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||||
@@ -29,7 +28,7 @@ const (
|
|||||||
SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED
|
SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED
|
||||||
|
|
||||||
//revive:disable-next-line:var-naming ALL_CAPS
|
//revive:disable-next-line:var-naming ALL_CAPS
|
||||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = windows.ERROR_NOT_ALL_ASSIGNED
|
ERROR_NOT_ALL_ASSIGNED windows.Errno = windows.ERROR_NOT_ALL_ASSIGNED
|
||||||
|
|
||||||
SeBackupPrivilege = "SeBackupPrivilege"
|
SeBackupPrivilege = "SeBackupPrivilege"
|
||||||
SeRestorePrivilege = "SeRestorePrivilege"
|
SeRestorePrivilege = "SeRestorePrivilege"
|
||||||
@@ -177,7 +176,7 @@ func newThreadToken() (windows.Token, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var token windows.Token
|
var token windows.Token
|
||||||
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
|
err = openThreadToken(getCurrentThread(), windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, false, &token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rerr := revertToSelf()
|
rerr := revertToSelf()
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
|
|||||||
+13
-24
@@ -5,7 +5,7 @@ package winio
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"syscall"
|
"fmt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@@ -15,10 +15,6 @@ import (
|
|||||||
//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
|
//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
|
||||||
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||||
//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
|
//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
|
||||||
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
|
||||||
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
|
||||||
//sys localFree(mem uintptr) = LocalFree
|
|
||||||
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
|
||||||
|
|
||||||
type AccountLookupError struct {
|
type AccountLookupError struct {
|
||||||
Name string
|
Name string
|
||||||
@@ -64,7 +60,7 @@ func LookupSidByName(name string) (sid string, err error) {
|
|||||||
|
|
||||||
var sidSize, sidNameUse, refDomainSize uint32
|
var sidSize, sidNameUse, refDomainSize uint32
|
||||||
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
||||||
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
|
if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
|
||||||
return "", &AccountLookupError{name, err}
|
return "", &AccountLookupError{name, err}
|
||||||
}
|
}
|
||||||
sidBuffer := make([]byte, sidSize)
|
sidBuffer := make([]byte, sidSize)
|
||||||
@@ -78,8 +74,8 @@ func LookupSidByName(name string) (sid string, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", &AccountLookupError{name, err}
|
return "", &AccountLookupError{name, err}
|
||||||
}
|
}
|
||||||
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
sid = windows.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
||||||
localFree(uintptr(unsafe.Pointer(strBuffer)))
|
_, _ = windows.LocalFree(windows.Handle(unsafe.Pointer(strBuffer)))
|
||||||
return sid, nil
|
return sid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +96,7 @@ func LookupNameBySid(sid string) (name string, err error) {
|
|||||||
if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil {
|
if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil {
|
||||||
return "", &AccountLookupError{sid, err}
|
return "", &AccountLookupError{sid, err}
|
||||||
}
|
}
|
||||||
defer localFree(uintptr(unsafe.Pointer(sidPtr)))
|
defer windows.LocalFree(windows.Handle(unsafe.Pointer(sidPtr))) //nolint:errcheck
|
||||||
|
|
||||||
var nameSize, refDomainSize, sidNameUse uint32
|
var nameSize, refDomainSize, sidNameUse uint32
|
||||||
err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse)
|
err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse)
|
||||||
@@ -120,25 +116,18 @@ func LookupNameBySid(sid string) (name string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
||||||
var sdBuffer uintptr
|
sd, err := windows.SecurityDescriptorFromString(sddl)
|
||||||
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &SddlConversionError{sddl, err}
|
return nil, &SddlConversionError{Sddl: sddl, Err: err}
|
||||||
}
|
}
|
||||||
defer localFree(sdBuffer)
|
b := unsafe.Slice((*byte)(unsafe.Pointer(sd)), sd.Length())
|
||||||
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
|
return b, nil
|
||||||
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
|
|
||||||
return sd, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
||||||
var sddl *uint16
|
if l := int(unsafe.Sizeof(windows.SECURITY_DESCRIPTOR{})); len(sd) < l {
|
||||||
// The returned string length seems to include an arbitrary number of terminating NULs.
|
return "", fmt.Errorf("SecurityDescriptor (%d) smaller than expected (%d): %w", len(sd), l, windows.ERROR_INCORRECT_SIZE)
|
||||||
// Don't use it.
|
|
||||||
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
defer localFree(uintptr(unsafe.Pointer(sddl)))
|
s := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&sd[0]))
|
||||||
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
|
return s.String(), nil
|
||||||
}
|
}
|
||||||
|
|||||||
-5
@@ -1,5 +0,0 @@
|
|||||||
//go:build tools
|
|
||||||
|
|
||||||
package winio
|
|
||||||
|
|
||||||
import _ "golang.org/x/tools/cmd/stringer"
|
|
||||||
+85
-126
@@ -33,9 +33,6 @@ func errnoErr(e syscall.Errno) error {
|
|||||||
case errnoERROR_IO_PENDING:
|
case errnoERROR_IO_PENDING:
|
||||||
return errERROR_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
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,38 +42,34 @@ var (
|
|||||||
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||||
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
||||||
|
|
||||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||||
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
||||||
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
|
||||||
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||||
procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
|
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
||||||
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
|
procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
|
||||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
||||||
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
||||||
procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
|
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||||
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||||
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
procBackupRead = modkernel32.NewProc("BackupRead")
|
||||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
||||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||||
procBackupRead = modkernel32.NewProc("BackupRead")
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||||
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe")
|
||||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
||||||
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
|
||||||
procLocalFree = modkernel32.NewProc("LocalFree")
|
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
|
||||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
|
||||||
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
|
||||||
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
|
|
||||||
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
|
|
||||||
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
|
|
||||||
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
||||||
@@ -84,7 +77,7 @@ func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, ou
|
|||||||
if releaseAll {
|
if releaseAll {
|
||||||
_p0 = 1
|
_p0 = 1
|
||||||
}
|
}
|
||||||
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)))
|
r0, _, e1 := syscall.SyscallN(procAdjustTokenPrivileges.Addr(), uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||||
success = r0 != 0
|
success = r0 != 0
|
||||||
if true {
|
if true {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
@@ -92,33 +85,8 @@ func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, ou
|
|||||||
return
|
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 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
|
r1, _, e1 := syscall.SyscallN(procConvertSidToStringSidW.Addr(), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)))
|
||||||
if r1 == 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
|
|
||||||
var _p0 *uint16
|
|
||||||
_p0, err = syscall.UTF16PtrFromString(str)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -126,21 +94,15 @@ func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision
|
|||||||
}
|
}
|
||||||
|
|
||||||
func convertStringSidToSid(str *uint16, sid **byte) (err error) {
|
func convertStringSidToSid(str *uint16, sid **byte) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)), 0)
|
r1, _, e1 := syscall.SyscallN(procConvertStringSidToSidW.Addr(), uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
|
|
||||||
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
|
|
||||||
len = uint32(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func impersonateSelf(level uint32) (err error) {
|
func impersonateSelf(level uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procImpersonateSelf.Addr(), uintptr(level))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -157,7 +119,7 @@ func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSiz
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
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)
|
r1, _, e1 := syscall.SyscallN(procLookupAccountNameW.Addr(), 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)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -165,7 +127,7 @@ func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidS
|
|||||||
}
|
}
|
||||||
|
|
||||||
func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procLookupAccountSidW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -182,7 +144,7 @@ func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
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)
|
r1, _, e1 := syscall.SyscallN(procLookupPrivilegeDisplayNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -199,7 +161,7 @@ func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
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.SyscallN(procLookupPrivilegeNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -221,19 +183,19 @@ func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
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)))
|
r1, _, e1 := syscall.SyscallN(procLookupPrivilegeValueW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
func openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
||||||
var _p0 uint32
|
var _p0 uint32
|
||||||
if openAsSelf {
|
if openAsSelf {
|
||||||
_p0 = 1
|
_p0 = 1
|
||||||
}
|
}
|
||||||
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procOpenThreadToken.Addr(), uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -241,14 +203,14 @@ func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func revertToSelf() (err error) {
|
func revertToSelf() (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
r1, _, e1 := syscall.SyscallN(procRevertToSelf.Addr())
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
func backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
var _p0 *byte
|
var _p0 *byte
|
||||||
if len(b) > 0 {
|
if len(b) > 0 {
|
||||||
_p0 = &b[0]
|
_p0 = &b[0]
|
||||||
@@ -261,14 +223,14 @@ func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, proce
|
|||||||
if processSecurity {
|
if processSecurity {
|
||||||
_p2 = 1
|
_p2 = 1
|
||||||
}
|
}
|
||||||
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)
|
r1, _, e1 := syscall.SyscallN(procBackupRead.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
func backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
var _p0 *byte
|
var _p0 *byte
|
||||||
if len(b) > 0 {
|
if len(b) > 0 {
|
||||||
_p0 = &b[0]
|
_p0 = &b[0]
|
||||||
@@ -281,39 +243,39 @@ func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, p
|
|||||||
if processSecurity {
|
if processSecurity {
|
||||||
_p2 = 1
|
_p2 = 1
|
||||||
}
|
}
|
||||||
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)
|
r1, _, e1 := syscall.SyscallN(procBackupWrite.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
func cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
r1, _, e1 := syscall.SyscallN(procCancelIoEx.Addr(), uintptr(file), uintptr(unsafe.Pointer(o)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
func connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
r1, _, e1 := syscall.SyscallN(procConnectNamedPipe.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(o)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
func createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) {
|
||||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
r0, _, e1 := syscall.SyscallN(procCreateIoCompletionPort.Addr(), uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount))
|
||||||
newport = syscall.Handle(r0)
|
newport = windows.Handle(r0)
|
||||||
if newport == 0 {
|
if newport == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
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) {
|
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
|
||||||
var _p0 *uint16
|
var _p0 *uint16
|
||||||
_p0, err = syscall.UTF16PtrFromString(name)
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -322,96 +284,93 @@ func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances ui
|
|||||||
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
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) {
|
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.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)
|
r0, _, e1 := syscall.SyscallN(procCreateNamedPipeW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)))
|
||||||
handle = syscall.Handle(r0)
|
handle = windows.Handle(r0)
|
||||||
if handle == syscall.InvalidHandle {
|
if handle == windows.InvalidHandle {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentThread() (h syscall.Handle) {
|
func disconnectNamedPipe(pipe windows.Handle) (err error) {
|
||||||
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
r1, _, e1 := syscall.SyscallN(procDisconnectNamedPipe.Addr(), uintptr(pipe))
|
||||||
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 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
func getCurrentThread() (h windows.Handle) {
|
||||||
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)
|
r0, _, _ := syscall.SyscallN(procGetCurrentThread.Addr())
|
||||||
|
h = windows.Handle(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procGetNamedPipeHandleStateW.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
func getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *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)
|
r1, _, e1 := syscall.SyscallN(procGetNamedPipeInfo.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
|
func getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||||
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
|
r1, _, e1 := syscall.SyscallN(procGetQueuedCompletionStatus.Addr(), uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout))
|
||||||
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 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
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) {
|
func setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) {
|
||||||
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)
|
r1, _, e1 := syscall.SyscallN(procSetFileCompletionNotificationModes.Addr(), uintptr(h), uintptr(flags))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) {
|
||||||
|
r0, _, _ := syscall.SyscallN(procNtCreateNamedPipeFile.Addr(), 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)))
|
||||||
status = ntStatus(r0)
|
status = ntStatus(r0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) {
|
func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) {
|
||||||
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
|
r0, _, _ := syscall.SyscallN(procRtlDefaultNpAcl.Addr(), uintptr(unsafe.Pointer(dacl)))
|
||||||
status = ntStatus(r0)
|
status = ntStatus(r0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) {
|
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)
|
r0, _, _ := syscall.SyscallN(procRtlDosPathNameToNtPathName_U.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved))
|
||||||
status = ntStatus(r0)
|
status = ntStatus(r0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func rtlNtStatusToDosError(status ntStatus) (winerr error) {
|
func rtlNtStatusToDosError(status ntStatus) (winerr error) {
|
||||||
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
|
r0, _, _ := syscall.SyscallN(procRtlNtStatusToDosErrorNoTeb.Addr(), uintptr(status))
|
||||||
if r0 != 0 {
|
if r0 != 0 {
|
||||||
winerr = syscall.Errno(r0)
|
winerr = syscall.Errno(r0)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
|
func wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
|
||||||
var _p0 uint32
|
var _p0 uint32
|
||||||
if wait {
|
if wait {
|
||||||
_p0 = 1
|
_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)
|
r1, _, e1 := syscall.SyscallN(procWSAGetOverlappedResult.Addr(), uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|||||||
-92
@@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The containerd 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 defines the common errors used throughout containerd
|
|
||||||
// packages.
|
|
||||||
//
|
|
||||||
// Use with fmt.Errorf to add context to an error.
|
|
||||||
//
|
|
||||||
// To detect an error class, use the IsXXX functions to tell whether an error
|
|
||||||
// is of a certain type.
|
|
||||||
//
|
|
||||||
// The functions ToGRPC and FromGRPC can be used to map server-side and
|
|
||||||
// client-side errors to the correct types.
|
|
||||||
package errdefs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Definitions of common error types used throughout containerd. All containerd
|
|
||||||
// errors returned by most packages will map into one of these errors classes.
|
|
||||||
// Packages should return errors of these types when they want to instruct a
|
|
||||||
// client to take a particular action.
|
|
||||||
//
|
|
||||||
// For the most part, we just try to provide local grpc errors. Most conditions
|
|
||||||
// map very well to those defined by grpc.
|
|
||||||
var (
|
|
||||||
ErrUnknown = errors.New("unknown") // used internally to represent a missed mapping.
|
|
||||||
ErrInvalidArgument = errors.New("invalid argument")
|
|
||||||
ErrNotFound = errors.New("not found")
|
|
||||||
ErrAlreadyExists = errors.New("already exists")
|
|
||||||
ErrFailedPrecondition = errors.New("failed precondition")
|
|
||||||
ErrUnavailable = errors.New("unavailable")
|
|
||||||
ErrNotImplemented = errors.New("not implemented") // represents not supported and unimplemented
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsInvalidArgument returns true if the error is due to an invalid argument
|
|
||||||
func IsInvalidArgument(err error) bool {
|
|
||||||
return errors.Is(err, ErrInvalidArgument)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNotFound returns true if the error is due to a missing object
|
|
||||||
func IsNotFound(err error) bool {
|
|
||||||
return errors.Is(err, ErrNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAlreadyExists returns true if the error is due to an already existing
|
|
||||||
// metadata item
|
|
||||||
func IsAlreadyExists(err error) bool {
|
|
||||||
return errors.Is(err, ErrAlreadyExists)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFailedPrecondition returns true if an operation could not proceed to the
|
|
||||||
// lack of a particular condition
|
|
||||||
func IsFailedPrecondition(err error) bool {
|
|
||||||
return errors.Is(err, ErrFailedPrecondition)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsUnavailable returns true if the error is due to a resource being unavailable
|
|
||||||
func IsUnavailable(err error) bool {
|
|
||||||
return errors.Is(err, ErrUnavailable)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNotImplemented returns true if the error is due to not being implemented
|
|
||||||
func IsNotImplemented(err error) bool {
|
|
||||||
return errors.Is(err, ErrNotImplemented)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsCanceled returns true if the error is due to `context.Canceled`.
|
|
||||||
func IsCanceled(err error) bool {
|
|
||||||
return errors.Is(err, context.Canceled)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDeadlineExceeded returns true if the error is due to
|
|
||||||
// `context.DeadlineExceeded`.
|
|
||||||
func IsDeadlineExceeded(err error) bool {
|
|
||||||
return errors.Is(err, context.DeadlineExceeded)
|
|
||||||
}
|
|
||||||
-147
@@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The containerd 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 (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ToGRPC will attempt to map the backend containerd error into a grpc error,
|
|
||||||
// using the original error message as a description.
|
|
||||||
//
|
|
||||||
// Further information may be extracted from certain errors depending on their
|
|
||||||
// type.
|
|
||||||
//
|
|
||||||
// If the error is unmapped, the original error will be returned to be handled
|
|
||||||
// by the regular grpc error handling stack.
|
|
||||||
func ToGRPC(err error) error {
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if isGRPCError(err) {
|
|
||||||
// error has already been mapped to grpc
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case IsInvalidArgument(err):
|
|
||||||
return status.Errorf(codes.InvalidArgument, err.Error())
|
|
||||||
case IsNotFound(err):
|
|
||||||
return status.Errorf(codes.NotFound, err.Error())
|
|
||||||
case IsAlreadyExists(err):
|
|
||||||
return status.Errorf(codes.AlreadyExists, err.Error())
|
|
||||||
case IsFailedPrecondition(err):
|
|
||||||
return status.Errorf(codes.FailedPrecondition, err.Error())
|
|
||||||
case IsUnavailable(err):
|
|
||||||
return status.Errorf(codes.Unavailable, err.Error())
|
|
||||||
case IsNotImplemented(err):
|
|
||||||
return status.Errorf(codes.Unimplemented, err.Error())
|
|
||||||
case IsCanceled(err):
|
|
||||||
return status.Errorf(codes.Canceled, err.Error())
|
|
||||||
case IsDeadlineExceeded(err):
|
|
||||||
return status.Errorf(codes.DeadlineExceeded, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToGRPCf maps the error to grpc error codes, assembling the formatting string
|
|
||||||
// and combining it with the target error string.
|
|
||||||
//
|
|
||||||
// This is equivalent to errdefs.ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err))
|
|
||||||
func ToGRPCf(err error, format string, args ...interface{}) error {
|
|
||||||
return ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromGRPC returns the underlying error from a grpc service based on the grpc error code
|
|
||||||
func FromGRPC(err error) error {
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var cls error // divide these into error classes, becomes the cause
|
|
||||||
|
|
||||||
switch code(err) {
|
|
||||||
case codes.InvalidArgument:
|
|
||||||
cls = ErrInvalidArgument
|
|
||||||
case codes.AlreadyExists:
|
|
||||||
cls = ErrAlreadyExists
|
|
||||||
case codes.NotFound:
|
|
||||||
cls = ErrNotFound
|
|
||||||
case codes.Unavailable:
|
|
||||||
cls = ErrUnavailable
|
|
||||||
case codes.FailedPrecondition:
|
|
||||||
cls = ErrFailedPrecondition
|
|
||||||
case codes.Unimplemented:
|
|
||||||
cls = ErrNotImplemented
|
|
||||||
case codes.Canceled:
|
|
||||||
cls = context.Canceled
|
|
||||||
case codes.DeadlineExceeded:
|
|
||||||
cls = context.DeadlineExceeded
|
|
||||||
default:
|
|
||||||
cls = ErrUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := rebaseMessage(cls, err)
|
|
||||||
if msg != "" {
|
|
||||||
err = fmt.Errorf("%s: %w", msg, cls)
|
|
||||||
} else {
|
|
||||||
err = cls
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// rebaseMessage removes the repeats for an error at the end of an error
|
|
||||||
// string. This will happen when taking an error over grpc then remapping it.
|
|
||||||
//
|
|
||||||
// Effectively, we just remove the string of cls from the end of err if it
|
|
||||||
// appears there.
|
|
||||||
func rebaseMessage(cls error, err error) string {
|
|
||||||
desc := errDesc(err)
|
|
||||||
clss := cls.Error()
|
|
||||||
if desc == clss {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimSuffix(desc, ": "+clss)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isGRPCError(err error) bool {
|
|
||||||
_, ok := status.FromError(err)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func code(err error) codes.Code {
|
|
||||||
if s, ok := status.FromError(err); ok {
|
|
||||||
return s.Code()
|
|
||||||
}
|
|
||||||
return codes.Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
func errDesc(err error) string {
|
|
||||||
if s, ok := status.FromError(err); ok {
|
|
||||||
return s.Message()
|
|
||||||
}
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
-149
@@ -1,149 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The containerd 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 log
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/containerd/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// G is a shorthand for [GetLogger].
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.G].
|
|
||||||
var G = log.G
|
|
||||||
|
|
||||||
// L is an alias for the standard logger.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.L].
|
|
||||||
var L = log.L
|
|
||||||
|
|
||||||
// Fields type to pass to "WithFields".
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.Fields].
|
|
||||||
type Fields = log.Fields
|
|
||||||
|
|
||||||
// Entry is a logging entry.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.Entry].
|
|
||||||
type Entry = log.Entry
|
|
||||||
|
|
||||||
// RFC3339NanoFixed is [time.RFC3339Nano] with nanoseconds padded using
|
|
||||||
// zeros to ensure the formatted time is always the same number of
|
|
||||||
// characters.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.RFC3339NanoFixed].
|
|
||||||
const RFC3339NanoFixed = log.RFC3339NanoFixed
|
|
||||||
|
|
||||||
// Level is a logging level.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.Level].
|
|
||||||
type Level = log.Level
|
|
||||||
|
|
||||||
// Supported log levels.
|
|
||||||
const (
|
|
||||||
// TraceLevel level.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.TraceLevel].
|
|
||||||
TraceLevel Level = log.TraceLevel
|
|
||||||
|
|
||||||
// DebugLevel level.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.DebugLevel].
|
|
||||||
DebugLevel Level = log.DebugLevel
|
|
||||||
|
|
||||||
// InfoLevel level.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.InfoLevel].
|
|
||||||
InfoLevel Level = log.InfoLevel
|
|
||||||
|
|
||||||
// WarnLevel level.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.WarnLevel].
|
|
||||||
WarnLevel Level = log.WarnLevel
|
|
||||||
|
|
||||||
// ErrorLevel level
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.ErrorLevel].
|
|
||||||
ErrorLevel Level = log.ErrorLevel
|
|
||||||
|
|
||||||
// FatalLevel level.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.FatalLevel].
|
|
||||||
FatalLevel Level = log.FatalLevel
|
|
||||||
|
|
||||||
// PanicLevel level.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.PanicLevel].
|
|
||||||
PanicLevel Level = log.PanicLevel
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetLevel sets log level globally. It returns an error if the given
|
|
||||||
// level is not supported.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.SetLevel].
|
|
||||||
func SetLevel(level string) error {
|
|
||||||
return log.SetLevel(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLevel returns the current log level.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.GetLevel].
|
|
||||||
func GetLevel() log.Level {
|
|
||||||
return log.GetLevel()
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputFormat specifies a log output format.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.OutputFormat].
|
|
||||||
type OutputFormat = log.OutputFormat
|
|
||||||
|
|
||||||
// Supported log output formats.
|
|
||||||
const (
|
|
||||||
// TextFormat represents the text logging format.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.TextFormat].
|
|
||||||
TextFormat log.OutputFormat = "text"
|
|
||||||
|
|
||||||
// JSONFormat represents the JSON logging format.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.JSONFormat].
|
|
||||||
JSONFormat log.OutputFormat = "json"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetFormat sets the log output format.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.SetFormat].
|
|
||||||
func SetFormat(format OutputFormat) error {
|
|
||||||
return log.SetFormat(format)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithLogger returns a new context with the provided logger. Use in
|
|
||||||
// combination with logger.WithField(s) for great effect.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.WithLogger].
|
|
||||||
func WithLogger(ctx context.Context, logger *log.Entry) context.Context {
|
|
||||||
return log.WithLogger(ctx, logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLogger retrieves the current logger from the context. If no logger is
|
|
||||||
// available, the default logger is returned.
|
|
||||||
//
|
|
||||||
// Deprecated: use [log.GetLogger].
|
|
||||||
func GetLogger(ctx context.Context) *log.Entry {
|
|
||||||
return log.GetLogger(ctx)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
*.go text eol=lf
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- exportloopref # Checks for pointers to enclosing loop variables
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
- gosec
|
||||||
|
- ineffassign
|
||||||
|
- misspell
|
||||||
|
- nolintlint
|
||||||
|
- revive
|
||||||
|
- staticcheck
|
||||||
|
- tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17
|
||||||
|
- unconvert
|
||||||
|
- unused
|
||||||
|
- vet
|
||||||
|
- dupword # Checks for duplicate words in the source code
|
||||||
|
disable:
|
||||||
|
- errcheck
|
||||||
|
|
||||||
|
run:
|
||||||
|
timeout: 5m
|
||||||
|
skip-dirs:
|
||||||
|
- api
|
||||||
|
- cluster
|
||||||
|
- design
|
||||||
|
- docs
|
||||||
|
- docs/man
|
||||||
|
- releases
|
||||||
|
- reports
|
||||||
|
- test # e2e scripts
|
||||||
+191
@@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright The containerd 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
|
||||||
|
|
||||||
|
https://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.
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
# platforms
|
||||||
|
|
||||||
|
A Go package for formatting, normalizing and matching container platforms.
|
||||||
|
|
||||||
|
This package is based on the Open Containers Image Spec definition of a [platform](https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/descriptor.go#L52).
|
||||||
|
|
||||||
|
## Platform Specifier
|
||||||
|
|
||||||
|
While the OCI platform specifications provide a tool for components to
|
||||||
|
specify structured information, user input typically doesn't need the full
|
||||||
|
context and much can be inferred. To solve this problem, this package introduces
|
||||||
|
"specifiers". A specifier has the format
|
||||||
|
`<os>|<arch>|<os>/<arch>[/<variant>]`. The user can provide either the
|
||||||
|
operating system or the architecture or both.
|
||||||
|
|
||||||
|
An example of a common specifier is `linux/amd64`. If the host has a default
|
||||||
|
runtime that matches this, the user can simply provide the component that
|
||||||
|
matters. For example, if an image provides `amd64` and `arm64` support, the
|
||||||
|
operating system, `linux` can be inferred, so they only have to provide
|
||||||
|
`arm64` or `amd64`. Similar behavior is implemented for operating systems,
|
||||||
|
where the architecture may be known but a runtime may support images from
|
||||||
|
different operating systems.
|
||||||
|
|
||||||
|
## Project details
|
||||||
|
|
||||||
|
**platforms** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
|
||||||
|
As a containerd sub-project, you will find the:
|
||||||
|
* [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
|
||||||
|
* [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
|
||||||
|
* and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
information in our [`containerd/project`](https://github.com/containerd/project) repository.
|
||||||
Generated
Vendored
Generated
Vendored
+1
-1
@@ -20,7 +20,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Present the ARM instruction set architecture, eg: v7, v8
|
// Present the ARM instruction set architecture, eg: v7, v8
|
||||||
Generated
Vendored
+6
-7
@@ -19,12 +19,12 @@ package platforms
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ func getCPUInfo(pattern string) (info string, err error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", fmt.Errorf("getCPUInfo for pattern %s: %w", pattern, errdefs.ErrNotFound)
|
return "", fmt.Errorf("getCPUInfo for pattern %s: %w", pattern, errNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCPUVariantFromArch get CPU variant from arch through a system call
|
// getCPUVariantFromArch get CPU variant from arch through a system call
|
||||||
@@ -83,7 +83,7 @@ func getCPUVariantFromArch(arch string) (string, error) {
|
|||||||
if arch == "aarch64" {
|
if arch == "aarch64" {
|
||||||
variant = "8"
|
variant = "8"
|
||||||
} else if arch[0:4] == "armv" && len(arch) >= 5 {
|
} else if arch[0:4] == "armv" && len(arch) >= 5 {
|
||||||
//Valid arch format is in form of armvXx
|
// Valid arch format is in form of armvXx
|
||||||
switch arch[3:5] {
|
switch arch[3:5] {
|
||||||
case "v8":
|
case "v8":
|
||||||
variant = "8"
|
variant = "8"
|
||||||
@@ -101,7 +101,7 @@ func getCPUVariantFromArch(arch string) (string, error) {
|
|||||||
variant = "unknown"
|
variant = "unknown"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return "", fmt.Errorf("getCPUVariantFromArch invalid arch: %s, %w", arch, errdefs.ErrInvalidArgument)
|
return "", fmt.Errorf("getCPUVariantFromArch invalid arch: %s, %w", arch, errInvalidArgument)
|
||||||
}
|
}
|
||||||
return variant, nil
|
return variant, nil
|
||||||
}
|
}
|
||||||
@@ -112,11 +112,10 @@ func getCPUVariantFromArch(arch string) (string, error) {
|
|||||||
// This is to cover running ARM in emulated environment on x86 host as this field in /proc/cpuinfo
|
// This is to cover running ARM in emulated environment on x86 host as this field in /proc/cpuinfo
|
||||||
// was not present.
|
// was not present.
|
||||||
func getCPUVariant() (string, error) {
|
func getCPUVariant() (string, error) {
|
||||||
|
|
||||||
variant, err := getCPUInfo("Cpu architecture")
|
variant, err := getCPUInfo("Cpu architecture")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errdefs.IsNotFound(err) {
|
if errors.Is(err, errNotFound) {
|
||||||
//Let's try getting CPU variant from machine architecture
|
// Let's try getting CPU variant from machine architecture
|
||||||
arch, err := getMachineArch()
|
arch, err := getMachineArch()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failure getting machine architecture: %v", err)
|
return "", fmt.Errorf("failure getting machine architecture: %v", err)
|
||||||
Generated
Vendored
+1
-5
@@ -21,8 +21,6 @@ package platforms
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getCPUVariant() (string, error) {
|
func getCPUVariant() (string, error) {
|
||||||
@@ -49,10 +47,8 @@ func getCPUVariant() (string, error) {
|
|||||||
default:
|
default:
|
||||||
variant = "unknown"
|
variant = "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errdefs.ErrNotImplemented)
|
return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return variant, nil
|
return variant, nil
|
||||||
Generated
Vendored
Generated
Vendored
+4
-2
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
package platforms
|
package platforms
|
||||||
|
|
||||||
// DefaultString returns the default string specifier for the platform.
|
// DefaultString returns the default string specifier for the platform,
|
||||||
|
// with [PR#6](https://github.com/containerd/platforms/pull/6) the result
|
||||||
|
// may now also include the OSVersion from the provided platform specification.
|
||||||
func DefaultString() string {
|
func DefaultString() string {
|
||||||
return Format(DefaultSpec())
|
return FormatAll(DefaultSpec())
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultStrict returns strict form of Default.
|
// DefaultStrict returns strict form of Default.
|
||||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
+30
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd 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 platforms
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// These errors mirror the errors defined in [github.com/containerd/containerd/errdefs],
|
||||||
|
// however, they are not exported as they are not expected to be used as sentinel
|
||||||
|
// errors by consumers of this package.
|
||||||
|
//
|
||||||
|
//nolint:unused // not all errors are used on all platforms.
|
||||||
|
var (
|
||||||
|
errNotFound = errors.New("not found")
|
||||||
|
errInvalidArgument = errors.New("invalid argument")
|
||||||
|
errNotImplemented = errors.New("not implemented")
|
||||||
|
)
|
||||||
Generated
Vendored
+64
-32
@@ -102,6 +102,9 @@
|
|||||||
// unless it is explicitly provided. This is treated as equivalent to armhf. A
|
// unless it is explicitly provided. This is treated as equivalent to armhf. A
|
||||||
// previous architecture, armel, will be normalized to arm/v6.
|
// previous architecture, armel, will be normalized to arm/v6.
|
||||||
//
|
//
|
||||||
|
// Similarly, the most common arm64 version v8, and most common amd64 version v1
|
||||||
|
// are represented without the variant.
|
||||||
|
//
|
||||||
// While these normalizations are provided, their support on arm platforms has
|
// While these normalizations are provided, their support on arm platforms has
|
||||||
// not yet been fully implemented and tested.
|
// not yet been fully implemented and tested.
|
||||||
package platforms
|
package platforms
|
||||||
@@ -115,14 +118,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
|
specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
|
||||||
|
osAndVersionRe = regexp.MustCompile(`^([A-Za-z0-9_-]+)(?:\(([A-Za-z0-9_.-]*)\))?$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const osAndVersionFormat = "%s(%s)"
|
||||||
|
|
||||||
// Platform is a type alias for convenience, so there is no need to import image-spec package everywhere.
|
// Platform is a type alias for convenience, so there is no need to import image-spec package everywhere.
|
||||||
type Platform = specs.Platform
|
type Platform = specs.Platform
|
||||||
|
|
||||||
@@ -155,40 +159,68 @@ func (m *matcher) Match(platform specs.Platform) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *matcher) String() string {
|
func (m *matcher) String() string {
|
||||||
return Format(m.Platform)
|
return FormatAll(m.Platform)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAll parses a list of platform specifiers into a list of platform.
|
||||||
|
func ParseAll(specifiers []string) ([]specs.Platform, error) {
|
||||||
|
platforms := make([]specs.Platform, len(specifiers))
|
||||||
|
for i, s := range specifiers {
|
||||||
|
p, err := Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid platform %s: %w", s, err)
|
||||||
|
}
|
||||||
|
platforms[i] = p
|
||||||
|
}
|
||||||
|
return platforms, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses the platform specifier syntax into a platform declaration.
|
// Parse parses the platform specifier syntax into a platform declaration.
|
||||||
//
|
//
|
||||||
// Platform specifiers are in the format `<os>|<arch>|<os>/<arch>[/<variant>]`.
|
// Platform specifiers are in the format `<os>[(<OSVersion>)]|<arch>|<os>[(<OSVersion>)]/<arch>[/<variant>]`.
|
||||||
// The minimum required information for a platform specifier is the operating
|
// The minimum required information for a platform specifier is the operating
|
||||||
// system or architecture. If there is only a single string (no slashes), the
|
// system or architecture. The OSVersion can be part of the OS like `windows(10.0.17763)`
|
||||||
|
// When an OSVersion is specified, then specs.Platform.OSVersion is populated with that value,
|
||||||
|
// and an empty string otherwise.
|
||||||
|
// If there is only a single string (no slashes), the
|
||||||
// value will be matched against the known set of operating systems, then fall
|
// value will be matched against the known set of operating systems, then fall
|
||||||
// back to the known set of architectures. The missing component will be
|
// back to the known set of architectures. The missing component will be
|
||||||
// inferred based on the local environment.
|
// inferred based on the local environment.
|
||||||
func Parse(specifier string) (specs.Platform, error) {
|
func Parse(specifier string) (specs.Platform, error) {
|
||||||
if strings.Contains(specifier, "*") {
|
if strings.Contains(specifier, "*") {
|
||||||
// TODO(stevvooe): need to work out exact wildcard handling
|
// TODO(stevvooe): need to work out exact wildcard handling
|
||||||
return specs.Platform{}, fmt.Errorf("%q: wildcards not yet supported: %w", specifier, errdefs.ErrInvalidArgument)
|
return specs.Platform{}, fmt.Errorf("%q: wildcards not yet supported: %w", specifier, errInvalidArgument)
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(specifier, "/")
|
// Limit to 4 elements to prevent unbounded split
|
||||||
|
parts := strings.SplitN(specifier, "/", 4)
|
||||||
|
|
||||||
for _, part := range parts {
|
var p specs.Platform
|
||||||
if !specifierRe.MatchString(part) {
|
for i, part := range parts {
|
||||||
return specs.Platform{}, fmt.Errorf("%q is an invalid component of %q: platform specifier component must match %q: %w", part, specifier, specifierRe.String(), errdefs.ErrInvalidArgument)
|
if i == 0 {
|
||||||
|
// First element is <os>[(<OSVersion>)]
|
||||||
|
osVer := osAndVersionRe.FindStringSubmatch(part)
|
||||||
|
if osVer == nil {
|
||||||
|
return specs.Platform{}, fmt.Errorf("%q is an invalid OS component of %q: OSAndVersion specifier component must match %q: %w", part, specifier, osAndVersionRe.String(), errInvalidArgument)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.OS = normalizeOS(osVer[1])
|
||||||
|
p.OSVersion = osVer[2]
|
||||||
|
} else {
|
||||||
|
if !specifierRe.MatchString(part) {
|
||||||
|
return specs.Platform{}, fmt.Errorf("%q is an invalid component of %q: platform specifier component must match %q: %w", part, specifier, specifierRe.String(), errInvalidArgument)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var p specs.Platform
|
|
||||||
switch len(parts) {
|
switch len(parts) {
|
||||||
case 1:
|
case 1:
|
||||||
// in this case, we will test that the value might be an OS, then look
|
// in this case, we will test that the value might be an OS (with or
|
||||||
// it up. If it is not known, we'll treat it as an architecture. Since
|
// without the optional OSVersion specified) and look it up.
|
||||||
|
// If it is not known, we'll treat it as an architecture. Since
|
||||||
// we have very little information about the platform here, we are
|
// we have very little information about the platform here, we are
|
||||||
// going to be a little more strict if we don't know about the argument
|
// going to be a little more strict if we don't know about the argument
|
||||||
// value.
|
// value.
|
||||||
p.OS = normalizeOS(parts[0])
|
|
||||||
if isKnownOS(p.OS) {
|
if isKnownOS(p.OS) {
|
||||||
// picks a default architecture
|
// picks a default architecture
|
||||||
p.Architecture = runtime.GOARCH
|
p.Architecture = runtime.GOARCH
|
||||||
@@ -196,10 +228,6 @@ func Parse(specifier string) (specs.Platform, error) {
|
|||||||
p.Variant = cpuVariant()
|
p.Variant = cpuVariant()
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.OS == "windows" {
|
|
||||||
p.OSVersion = GetWindowsOsVersion()
|
|
||||||
}
|
|
||||||
|
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,37 +240,27 @@ func Parse(specifier string) (specs.Platform, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return specs.Platform{}, fmt.Errorf("%q: unknown operating system or architecture: %w", specifier, errdefs.ErrInvalidArgument)
|
return specs.Platform{}, fmt.Errorf("%q: unknown operating system or architecture: %w", specifier, errInvalidArgument)
|
||||||
case 2:
|
case 2:
|
||||||
// In this case, we treat as a regular os/arch pair. We don't care
|
// In this case, we treat as a regular OS[(OSVersion)]/arch pair. We don't care
|
||||||
// about whether or not we know of the platform.
|
// about whether or not we know of the platform.
|
||||||
p.OS = normalizeOS(parts[0])
|
|
||||||
p.Architecture, p.Variant = normalizeArch(parts[1], "")
|
p.Architecture, p.Variant = normalizeArch(parts[1], "")
|
||||||
if p.Architecture == "arm" && p.Variant == "v7" {
|
if p.Architecture == "arm" && p.Variant == "v7" {
|
||||||
p.Variant = ""
|
p.Variant = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.OS == "windows" {
|
|
||||||
p.OSVersion = GetWindowsOsVersion()
|
|
||||||
}
|
|
||||||
|
|
||||||
return p, nil
|
return p, nil
|
||||||
case 3:
|
case 3:
|
||||||
// we have a fully specified variant, this is rare
|
// we have a fully specified variant, this is rare
|
||||||
p.OS = normalizeOS(parts[0])
|
|
||||||
p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
|
p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
|
||||||
if p.Architecture == "arm64" && p.Variant == "" {
|
if p.Architecture == "arm64" && p.Variant == "" {
|
||||||
p.Variant = "v8"
|
p.Variant = "v8"
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.OS == "windows" {
|
|
||||||
p.OSVersion = GetWindowsOsVersion()
|
|
||||||
}
|
|
||||||
|
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return specs.Platform{}, fmt.Errorf("%q: cannot parse platform specifier: %w", specifier, errdefs.ErrInvalidArgument)
|
return specs.Platform{}, fmt.Errorf("%q: cannot parse platform specifier: %w", specifier, errInvalidArgument)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustParse is like Parses but panics if the specifier cannot be parsed.
|
// MustParse is like Parses but panics if the specifier cannot be parsed.
|
||||||
@@ -264,6 +282,20 @@ func Format(platform specs.Platform) string {
|
|||||||
return path.Join(platform.OS, platform.Architecture, platform.Variant)
|
return path.Join(platform.OS, platform.Architecture, platform.Variant)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormatAll returns a string specifier that also includes the OSVersion from the
|
||||||
|
// provided platform specification.
|
||||||
|
func FormatAll(platform specs.Platform) string {
|
||||||
|
if platform.OS == "" {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
if platform.OSVersion != "" {
|
||||||
|
OSAndVersion := fmt.Sprintf(osAndVersionFormat, platform.OS, platform.OSVersion)
|
||||||
|
return path.Join(OSAndVersion, platform.Architecture, platform.Variant)
|
||||||
|
}
|
||||||
|
return path.Join(platform.OS, platform.Architecture, platform.Variant)
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize validates and translate the platform to the canonical value.
|
// Normalize validates and translate the platform to the canonical value.
|
||||||
//
|
//
|
||||||
// For example, if "Aarch64" is encountered, we change it to "arm64" or if
|
// For example, if "Aarch64" is encountered, we change it to "arm64" or if
|
||||||
Generated
Vendored
-4
@@ -28,7 +28,3 @@ func newDefaultMatcher(platform specs.Platform) Matcher {
|
|||||||
Platform: Normalize(platform),
|
Platform: Normalize(platform),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetWindowsOsVersion() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
Generated
Vendored
-8
@@ -17,10 +17,7 @@
|
|||||||
package platforms
|
package platforms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewMatcher returns a Windows matcher that will match on osVersionPrefix if
|
// NewMatcher returns a Windows matcher that will match on osVersionPrefix if
|
||||||
@@ -35,8 +32,3 @@ func newDefaultMatcher(platform specs.Platform) Matcher {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetWindowsOsVersion() string {
|
|
||||||
major, minor, build := windows.RtlGetNtVersionNumbers()
|
|
||||||
return fmt.Sprintf("%d.%d.%d", major, minor, build)
|
|
||||||
}
|
|
||||||
+154
-62
@@ -2179,72 +2179,129 @@ definitions:
|
|||||||
type: "object"
|
type: "object"
|
||||||
properties:
|
properties:
|
||||||
Name:
|
Name:
|
||||||
|
description: |
|
||||||
|
Name of the network.
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "my_network"
|
||||||
Id:
|
Id:
|
||||||
|
description: |
|
||||||
|
ID that uniquely identifies a network on a single machine.
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99"
|
||||||
Created:
|
Created:
|
||||||
|
description: |
|
||||||
|
Date and time at which the network was created in
|
||||||
|
[RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds.
|
||||||
type: "string"
|
type: "string"
|
||||||
format: "dateTime"
|
format: "dateTime"
|
||||||
|
example: "2016-10-19T04:33:30.360899459Z"
|
||||||
Scope:
|
Scope:
|
||||||
|
description: |
|
||||||
|
The level at which the network exists (e.g. `swarm` for cluster-wide
|
||||||
|
or `local` for machine level)
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "local"
|
||||||
Driver:
|
Driver:
|
||||||
|
description: |
|
||||||
|
The name of the driver used to create the network (e.g. `bridge`,
|
||||||
|
`overlay`).
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "overlay"
|
||||||
EnableIPv6:
|
EnableIPv6:
|
||||||
|
description: |
|
||||||
|
Whether the network was created with IPv6 enabled.
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
|
example: false
|
||||||
IPAM:
|
IPAM:
|
||||||
$ref: "#/definitions/IPAM"
|
$ref: "#/definitions/IPAM"
|
||||||
Internal:
|
Internal:
|
||||||
|
description: |
|
||||||
|
Whether the network is created to only allow internal networking
|
||||||
|
connectivity.
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
|
default: false
|
||||||
|
example: false
|
||||||
Attachable:
|
Attachable:
|
||||||
|
description: |
|
||||||
|
Wheter a global / swarm scope network is manually attachable by regular
|
||||||
|
containers from workers in swarm mode.
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
|
default: false
|
||||||
|
example: false
|
||||||
Ingress:
|
Ingress:
|
||||||
|
description: |
|
||||||
|
Whether the network is providing the routing-mesh for the swarm cluster.
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
|
default: false
|
||||||
|
example: false
|
||||||
|
ConfigFrom:
|
||||||
|
$ref: "#/definitions/ConfigReference"
|
||||||
|
ConfigOnly:
|
||||||
|
description: |
|
||||||
|
Whether the network is a config-only network. Config-only networks are
|
||||||
|
placeholder networks for network configurations to be used by other
|
||||||
|
networks. Config-only networks cannot be used directly to run containers
|
||||||
|
or services.
|
||||||
|
type: "boolean"
|
||||||
|
default: false
|
||||||
Containers:
|
Containers:
|
||||||
|
description: |
|
||||||
|
Contains endpoints attached to the network.
|
||||||
type: "object"
|
type: "object"
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
$ref: "#/definitions/NetworkContainer"
|
$ref: "#/definitions/NetworkContainer"
|
||||||
|
example:
|
||||||
|
19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c:
|
||||||
|
Name: "test"
|
||||||
|
EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a"
|
||||||
|
MacAddress: "02:42:ac:13:00:02"
|
||||||
|
IPv4Address: "172.19.0.2/16"
|
||||||
|
IPv6Address: ""
|
||||||
Options:
|
Options:
|
||||||
|
description: |
|
||||||
|
Network-specific options uses when creating the network.
|
||||||
type: "object"
|
type: "object"
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example:
|
||||||
|
com.docker.network.bridge.default_bridge: "true"
|
||||||
|
com.docker.network.bridge.enable_icc: "true"
|
||||||
|
com.docker.network.bridge.enable_ip_masquerade: "true"
|
||||||
|
com.docker.network.bridge.host_binding_ipv4: "0.0.0.0"
|
||||||
|
com.docker.network.bridge.name: "docker0"
|
||||||
|
com.docker.network.driver.mtu: "1500"
|
||||||
Labels:
|
Labels:
|
||||||
|
description: "User-defined key/value metadata."
|
||||||
type: "object"
|
type: "object"
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
type: "string"
|
type: "string"
|
||||||
example:
|
example:
|
||||||
Name: "net01"
|
com.example.some-label: "some-value"
|
||||||
Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99"
|
com.example.some-other-label: "some-other-value"
|
||||||
Created: "2016-10-19T04:33:30.360899459Z"
|
Peers:
|
||||||
Scope: "local"
|
description: |
|
||||||
Driver: "bridge"
|
List of peer nodes for an overlay network. This field is only present
|
||||||
EnableIPv6: false
|
for overlay networks, and omitted for other network types.
|
||||||
IPAM:
|
type: "array"
|
||||||
Driver: "default"
|
items:
|
||||||
Config:
|
$ref: "#/definitions/PeerInfo"
|
||||||
- Subnet: "172.19.0.0/16"
|
x-nullable: true
|
||||||
Gateway: "172.19.0.1"
|
# TODO: Add Services (only present when "verbose" is set).
|
||||||
Options:
|
|
||||||
foo: "bar"
|
ConfigReference:
|
||||||
Internal: false
|
description: |
|
||||||
Attachable: false
|
The config-only network source to provide the configuration for
|
||||||
Ingress: false
|
this network.
|
||||||
Containers:
|
type: "object"
|
||||||
19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c:
|
properties:
|
||||||
Name: "test"
|
Network:
|
||||||
EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a"
|
description: |
|
||||||
MacAddress: "02:42:ac:13:00:02"
|
The name of the config-only network that provides the network's
|
||||||
IPv4Address: "172.19.0.2/16"
|
configuration. The specified network must be an existing config-only
|
||||||
IPv6Address: ""
|
network. Only network names are allowed, not network IDs.
|
||||||
Options:
|
type: "string"
|
||||||
com.docker.network.bridge.default_bridge: "true"
|
example: "config_only_network_01"
|
||||||
com.docker.network.bridge.enable_icc: "true"
|
|
||||||
com.docker.network.bridge.enable_ip_masquerade: "true"
|
|
||||||
com.docker.network.bridge.host_binding_ipv4: "0.0.0.0"
|
|
||||||
com.docker.network.bridge.name: "docker0"
|
|
||||||
com.docker.network.driver.mtu: "1500"
|
|
||||||
Labels:
|
|
||||||
com.example.some-label: "some-value"
|
|
||||||
com.example.some-other-label: "some-other-value"
|
|
||||||
IPAM:
|
IPAM:
|
||||||
type: "object"
|
type: "object"
|
||||||
properties:
|
properties:
|
||||||
@@ -2252,6 +2309,7 @@ definitions:
|
|||||||
description: "Name of the IPAM driver to use."
|
description: "Name of the IPAM driver to use."
|
||||||
type: "string"
|
type: "string"
|
||||||
default: "default"
|
default: "default"
|
||||||
|
example: "default"
|
||||||
Config:
|
Config:
|
||||||
description: |
|
description: |
|
||||||
List of IPAM configuration options, specified as a map:
|
List of IPAM configuration options, specified as a map:
|
||||||
@@ -2267,16 +2325,21 @@ definitions:
|
|||||||
type: "object"
|
type: "object"
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example:
|
||||||
|
foo: "bar"
|
||||||
|
|
||||||
IPAMConfig:
|
IPAMConfig:
|
||||||
type: "object"
|
type: "object"
|
||||||
properties:
|
properties:
|
||||||
Subnet:
|
Subnet:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "172.20.0.0/16"
|
||||||
IPRange:
|
IPRange:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "172.20.10.0/24"
|
||||||
Gateway:
|
Gateway:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "172.20.10.11"
|
||||||
AuxiliaryAddresses:
|
AuxiliaryAddresses:
|
||||||
type: "object"
|
type: "object"
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
@@ -2287,14 +2350,35 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
Name:
|
Name:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "container_1"
|
||||||
EndpointID:
|
EndpointID:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a"
|
||||||
MacAddress:
|
MacAddress:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "02:42:ac:13:00:02"
|
||||||
IPv4Address:
|
IPv4Address:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "172.19.0.2/16"
|
||||||
IPv6Address:
|
IPv6Address:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: ""
|
||||||
|
|
||||||
|
PeerInfo:
|
||||||
|
description: |
|
||||||
|
PeerInfo represents one peer of an overlay network.
|
||||||
|
type: "object"
|
||||||
|
properties:
|
||||||
|
Name:
|
||||||
|
description:
|
||||||
|
ID of the peer-node in the Swarm cluster.
|
||||||
|
type: "string"
|
||||||
|
example: "6869d7c1732b"
|
||||||
|
IP:
|
||||||
|
description:
|
||||||
|
IP-address of the peer-node in the Swarm cluster.
|
||||||
|
type: "string"
|
||||||
|
example: "10.133.77.91"
|
||||||
|
|
||||||
BuildInfo:
|
BuildInfo:
|
||||||
type: "object"
|
type: "object"
|
||||||
@@ -10104,14 +10188,22 @@ paths:
|
|||||||
Name:
|
Name:
|
||||||
description: "The network's name."
|
description: "The network's name."
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example: "my_network"
|
||||||
CheckDuplicate:
|
CheckDuplicate:
|
||||||
description: |
|
description: |
|
||||||
Deprecated: CheckDuplicate is now always enabled.
|
Deprecated: CheckDuplicate is now always enabled.
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
|
example: true
|
||||||
Driver:
|
Driver:
|
||||||
description: "Name of the network driver plugin to use."
|
description: "Name of the network driver plugin to use."
|
||||||
type: "string"
|
type: "string"
|
||||||
default: "bridge"
|
default: "bridge"
|
||||||
|
example: "bridge"
|
||||||
|
Scope:
|
||||||
|
description: |
|
||||||
|
The level at which the network exists (e.g. `swarm` for cluster-wide
|
||||||
|
or `local` for machine level).
|
||||||
|
type: "string"
|
||||||
Internal:
|
Internal:
|
||||||
description: "Restrict external access to the network."
|
description: "Restrict external access to the network."
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
@@ -10120,55 +10212,55 @@ paths:
|
|||||||
Globally scoped network is manually attachable by regular
|
Globally scoped network is manually attachable by regular
|
||||||
containers from workers in swarm mode.
|
containers from workers in swarm mode.
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
|
example: true
|
||||||
Ingress:
|
Ingress:
|
||||||
description: |
|
description: |
|
||||||
Ingress network is the network which provides the routing-mesh
|
Ingress network is the network which provides the routing-mesh
|
||||||
in swarm mode.
|
in swarm mode.
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
|
example: false
|
||||||
|
ConfigOnly:
|
||||||
|
description: |
|
||||||
|
Creates a config-only network. Config-only networks are placeholder
|
||||||
|
networks for network configurations to be used by other networks.
|
||||||
|
Config-only networks cannot be used directly to run containers
|
||||||
|
or services.
|
||||||
|
type: "boolean"
|
||||||
|
default: false
|
||||||
|
example: false
|
||||||
|
ConfigFrom:
|
||||||
|
description: |
|
||||||
|
Specifies the source which will provide the configuration for
|
||||||
|
this network. The specified network must be an existing
|
||||||
|
config-only network; see ConfigOnly.
|
||||||
|
$ref: "#/definitions/ConfigReference"
|
||||||
IPAM:
|
IPAM:
|
||||||
description: "Optional custom IP scheme for the network."
|
description: "Optional custom IP scheme for the network."
|
||||||
$ref: "#/definitions/IPAM"
|
$ref: "#/definitions/IPAM"
|
||||||
EnableIPv6:
|
EnableIPv6:
|
||||||
description: "Enable IPv6 on the network."
|
description: "Enable IPv6 on the network."
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
|
example: true
|
||||||
Options:
|
Options:
|
||||||
description: "Network specific options to be used by the drivers."
|
description: "Network specific options to be used by the drivers."
|
||||||
type: "object"
|
type: "object"
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
example:
|
||||||
|
com.docker.network.bridge.default_bridge: "true"
|
||||||
|
com.docker.network.bridge.enable_icc: "true"
|
||||||
|
com.docker.network.bridge.enable_ip_masquerade: "true"
|
||||||
|
com.docker.network.bridge.host_binding_ipv4: "0.0.0.0"
|
||||||
|
com.docker.network.bridge.name: "docker0"
|
||||||
|
com.docker.network.driver.mtu: "1500"
|
||||||
Labels:
|
Labels:
|
||||||
description: "User-defined key/value metadata."
|
description: "User-defined key/value metadata."
|
||||||
type: "object"
|
type: "object"
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
type: "string"
|
type: "string"
|
||||||
example:
|
example:
|
||||||
Name: "isolated_nw"
|
com.example.some-label: "some-value"
|
||||||
CheckDuplicate: false
|
com.example.some-other-label: "some-other-value"
|
||||||
Driver: "bridge"
|
|
||||||
EnableIPv6: true
|
|
||||||
IPAM:
|
|
||||||
Driver: "default"
|
|
||||||
Config:
|
|
||||||
- Subnet: "172.20.0.0/16"
|
|
||||||
IPRange: "172.20.10.0/24"
|
|
||||||
Gateway: "172.20.10.11"
|
|
||||||
- Subnet: "2001:db8:abcd::/64"
|
|
||||||
Gateway: "2001:db8:abcd::1011"
|
|
||||||
Options:
|
|
||||||
foo: "bar"
|
|
||||||
Internal: true
|
|
||||||
Attachable: false
|
|
||||||
Ingress: false
|
|
||||||
Options:
|
|
||||||
com.docker.network.bridge.default_bridge: "true"
|
|
||||||
com.docker.network.bridge.enable_icc: "true"
|
|
||||||
com.docker.network.bridge.enable_ip_masquerade: "true"
|
|
||||||
com.docker.network.bridge.host_binding_ipv4: "0.0.0.0"
|
|
||||||
com.docker.network.bridge.name: "docker0"
|
|
||||||
com.docker.network.driver.mtu: "1500"
|
|
||||||
Labels:
|
|
||||||
com.example.some-label: "some-value"
|
|
||||||
com.example.some-other-label: "some-other-value"
|
|
||||||
tags: ["Network"]
|
tags: ["Network"]
|
||||||
|
|
||||||
/networks/{id}/connect:
|
/networks/{id}/connect:
|
||||||
|
|||||||
+13
-13
@@ -457,24 +457,24 @@ type EndpointResource struct {
|
|||||||
type NetworkCreate struct {
|
type NetworkCreate struct {
|
||||||
// Deprecated: CheckDuplicate is deprecated since API v1.44, but it defaults to true when sent by the client
|
// Deprecated: CheckDuplicate is deprecated since API v1.44, but it defaults to true when sent by the client
|
||||||
// package to older daemons.
|
// package to older daemons.
|
||||||
CheckDuplicate bool `json:",omitempty"`
|
CheckDuplicate bool `json:",omitempty"`
|
||||||
Driver string
|
Driver string // Driver is the driver-name used to create the network (e.g. `bridge`, `overlay`)
|
||||||
Scope string
|
Scope string // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level).
|
||||||
EnableIPv6 bool
|
EnableIPv6 bool // EnableIPv6 represents whether to enable IPv6.
|
||||||
IPAM *network.IPAM
|
IPAM *network.IPAM // IPAM is the network's IP Address Management.
|
||||||
Internal bool
|
Internal bool // Internal represents if the network is used internal only.
|
||||||
Attachable bool
|
Attachable bool // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode.
|
||||||
Ingress bool
|
Ingress bool // Ingress indicates the network is providing the routing-mesh for the swarm cluster.
|
||||||
ConfigOnly bool
|
ConfigOnly bool // ConfigOnly creates a config-only network. Config-only networks are place-holder networks for network configurations to be used by other networks. ConfigOnly networks cannot be used directly to run containers or services.
|
||||||
ConfigFrom *network.ConfigReference
|
ConfigFrom *network.ConfigReference // ConfigFrom specifies the source which will provide the configuration for this network. The specified network must be a config-only network; see [NetworkCreate.ConfigOnly].
|
||||||
Options map[string]string
|
Options map[string]string // Options specifies the network-specific options to use for when creating the network.
|
||||||
Labels map[string]string
|
Labels map[string]string // Labels holds metadata specific to the network being created.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkCreateRequest is the request message sent to the server for network create call.
|
// NetworkCreateRequest is the request message sent to the server for network create call.
|
||||||
type NetworkCreateRequest struct {
|
type NetworkCreateRequest struct {
|
||||||
NetworkCreate
|
NetworkCreate
|
||||||
Name string
|
Name string // Name is the requested name of the network.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkCreateResponse is the response message sent by the server for network create call
|
// NetworkCreateResponse is the response message sent by the server for network create call
|
||||||
|
|||||||
+19
-9
@@ -21,12 +21,20 @@ const (
|
|||||||
// MediaTypeLayoutHeader specifies the media type for the oci-layout.
|
// MediaTypeLayoutHeader specifies the media type for the oci-layout.
|
||||||
MediaTypeLayoutHeader = "application/vnd.oci.layout.header.v1+json"
|
MediaTypeLayoutHeader = "application/vnd.oci.layout.header.v1+json"
|
||||||
|
|
||||||
// MediaTypeImageManifest specifies the media type for an image manifest.
|
|
||||||
MediaTypeImageManifest = "application/vnd.oci.image.manifest.v1+json"
|
|
||||||
|
|
||||||
// MediaTypeImageIndex specifies the media type for an image index.
|
// MediaTypeImageIndex specifies the media type for an image index.
|
||||||
MediaTypeImageIndex = "application/vnd.oci.image.index.v1+json"
|
MediaTypeImageIndex = "application/vnd.oci.image.index.v1+json"
|
||||||
|
|
||||||
|
// MediaTypeImageManifest specifies the media type for an image manifest.
|
||||||
|
MediaTypeImageManifest = "application/vnd.oci.image.manifest.v1+json"
|
||||||
|
|
||||||
|
// MediaTypeImageConfig specifies the media type for the image configuration.
|
||||||
|
MediaTypeImageConfig = "application/vnd.oci.image.config.v1+json"
|
||||||
|
|
||||||
|
// MediaTypeEmptyJSON specifies the media type for an unused blob containing the value "{}".
|
||||||
|
MediaTypeEmptyJSON = "application/vnd.oci.empty.v1+json"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
// MediaTypeImageLayer is the media type used for layers referenced by the manifest.
|
// MediaTypeImageLayer is the media type used for layers referenced by the manifest.
|
||||||
MediaTypeImageLayer = "application/vnd.oci.image.layer.v1.tar"
|
MediaTypeImageLayer = "application/vnd.oci.image.layer.v1.tar"
|
||||||
|
|
||||||
@@ -37,7 +45,15 @@ const (
|
|||||||
// MediaTypeImageLayerZstd is the media type used for zstd compressed
|
// MediaTypeImageLayerZstd is the media type used for zstd compressed
|
||||||
// layers referenced by the manifest.
|
// layers referenced by the manifest.
|
||||||
MediaTypeImageLayerZstd = "application/vnd.oci.image.layer.v1.tar+zstd"
|
MediaTypeImageLayerZstd = "application/vnd.oci.image.layer.v1.tar+zstd"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Non-distributable layer media-types.
|
||||||
|
//
|
||||||
|
// Deprecated: Non-distributable layers are deprecated, and not recommended
|
||||||
|
// for future use. Implementations SHOULD NOT produce new non-distributable
|
||||||
|
// layers.
|
||||||
|
// https://github.com/opencontainers/image-spec/pull/965
|
||||||
|
const (
|
||||||
// MediaTypeImageLayerNonDistributable is the media type for layers referenced by
|
// MediaTypeImageLayerNonDistributable is the media type for layers referenced by
|
||||||
// the manifest but with distribution restrictions.
|
// the manifest but with distribution restrictions.
|
||||||
//
|
//
|
||||||
@@ -66,10 +82,4 @@ const (
|
|||||||
// layers.
|
// layers.
|
||||||
// https://github.com/opencontainers/image-spec/pull/965
|
// https://github.com/opencontainers/image-spec/pull/965
|
||||||
MediaTypeImageLayerNonDistributableZstd = "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd"
|
MediaTypeImageLayerNonDistributableZstd = "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd"
|
||||||
|
|
||||||
// MediaTypeImageConfig specifies the media type for the image configuration.
|
|
||||||
MediaTypeImageConfig = "application/vnd.oci.image.config.v1+json"
|
|
||||||
|
|
||||||
// MediaTypeEmptyJSON specifies the media type for an unused blob containing the value `{}`
|
|
||||||
MediaTypeEmptyJSON = "application/vnd.oci.empty.v1+json"
|
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ const (
|
|||||||
VersionPatch = 0
|
VersionPatch = 0
|
||||||
|
|
||||||
// VersionDev indicates development branch. Releases will be empty string.
|
// VersionDev indicates development branch. Releases will be empty string.
|
||||||
VersionDev = "-rc.5"
|
VersionDev = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is the specification version that the package types support.
|
// Version is the specification version that the package types support.
|
||||||
|
|||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
Additional IP Rights Grant (Patents)
|
|
||||||
|
|
||||||
"This implementation" means the copyrightable works distributed by
|
|
||||||
Google as part of the Go project.
|
|
||||||
|
|
||||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
|
||||||
patent license to make, have made, use, offer to sell, sell, import,
|
|
||||||
transfer and otherwise run, modify and propagate the contents of this
|
|
||||||
implementation of Go, where such license applies only to those patent
|
|
||||||
claims, both currently owned or controlled by Google and acquired in
|
|
||||||
the future, licensable by Google that are necessarily infringed by this
|
|
||||||
implementation of Go. This grant does not include claims that would be
|
|
||||||
infringed only as a consequence of further modification of this
|
|
||||||
implementation. If you or your agent or exclusive licensee institute or
|
|
||||||
order or agree to the institution of patent litigation against any
|
|
||||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
|
||||||
that this implementation of Go or any code incorporated within this
|
|
||||||
implementation of Go constitutes direct or contributory patent
|
|
||||||
infringement, or inducement of patent infringement, then any patent
|
|
||||||
rights granted to you under this License for this implementation of Go
|
|
||||||
shall terminate as of the date such litigation is filed.
|
|
||||||
-401
@@ -1,401 +0,0 @@
|
|||||||
// Copyright 2018 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.
|
|
||||||
|
|
||||||
// Package semver implements comparison of semantic version strings.
|
|
||||||
// In this package, semantic version strings must begin with a leading "v",
|
|
||||||
// as in "v1.0.0".
|
|
||||||
//
|
|
||||||
// The general form of a semantic version string accepted by this package is
|
|
||||||
//
|
|
||||||
// vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]]
|
|
||||||
//
|
|
||||||
// where square brackets indicate optional parts of the syntax;
|
|
||||||
// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros;
|
|
||||||
// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers
|
|
||||||
// using only alphanumeric characters and hyphens; and
|
|
||||||
// all-numeric PRERELEASE identifiers must not have leading zeros.
|
|
||||||
//
|
|
||||||
// This package follows Semantic Versioning 2.0.0 (see semver.org)
|
|
||||||
// with two exceptions. First, it requires the "v" prefix. Second, it recognizes
|
|
||||||
// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
|
|
||||||
// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
|
|
||||||
package semver
|
|
||||||
|
|
||||||
import "sort"
|
|
||||||
|
|
||||||
// parsed returns the parsed form of a semantic version string.
|
|
||||||
type parsed struct {
|
|
||||||
major string
|
|
||||||
minor string
|
|
||||||
patch string
|
|
||||||
short string
|
|
||||||
prerelease string
|
|
||||||
build string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid reports whether v is a valid semantic version string.
|
|
||||||
func IsValid(v string) bool {
|
|
||||||
_, ok := parse(v)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Canonical returns the canonical formatting of the semantic version v.
|
|
||||||
// It fills in any missing .MINOR or .PATCH and discards build metadata.
|
|
||||||
// Two semantic versions compare equal only if their canonical formattings
|
|
||||||
// are identical strings.
|
|
||||||
// The canonical invalid semantic version is the empty string.
|
|
||||||
func Canonical(v string) string {
|
|
||||||
p, ok := parse(v)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if p.build != "" {
|
|
||||||
return v[:len(v)-len(p.build)]
|
|
||||||
}
|
|
||||||
if p.short != "" {
|
|
||||||
return v + p.short
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Major returns the major version prefix of the semantic version v.
|
|
||||||
// For example, Major("v2.1.0") == "v2".
|
|
||||||
// If v is an invalid semantic version string, Major returns the empty string.
|
|
||||||
func Major(v string) string {
|
|
||||||
pv, ok := parse(v)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return v[:1+len(pv.major)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MajorMinor returns the major.minor version prefix of the semantic version v.
|
|
||||||
// For example, MajorMinor("v2.1.0") == "v2.1".
|
|
||||||
// If v is an invalid semantic version string, MajorMinor returns the empty string.
|
|
||||||
func MajorMinor(v string) string {
|
|
||||||
pv, ok := parse(v)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
i := 1 + len(pv.major)
|
|
||||||
if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor {
|
|
||||||
return v[:j]
|
|
||||||
}
|
|
||||||
return v[:i] + "." + pv.minor
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prerelease returns the prerelease suffix of the semantic version v.
|
|
||||||
// For example, Prerelease("v2.1.0-pre+meta") == "-pre".
|
|
||||||
// If v is an invalid semantic version string, Prerelease returns the empty string.
|
|
||||||
func Prerelease(v string) string {
|
|
||||||
pv, ok := parse(v)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return pv.prerelease
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build returns the build suffix of the semantic version v.
|
|
||||||
// For example, Build("v2.1.0+meta") == "+meta".
|
|
||||||
// If v is an invalid semantic version string, Build returns the empty string.
|
|
||||||
func Build(v string) string {
|
|
||||||
pv, ok := parse(v)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return pv.build
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare returns an integer comparing two versions according to
|
|
||||||
// semantic version precedence.
|
|
||||||
// The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
|
|
||||||
//
|
|
||||||
// An invalid semantic version string is considered less than a valid one.
|
|
||||||
// All invalid semantic version strings compare equal to each other.
|
|
||||||
func Compare(v, w string) int {
|
|
||||||
pv, ok1 := parse(v)
|
|
||||||
pw, ok2 := parse(w)
|
|
||||||
if !ok1 && !ok2 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if !ok1 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if !ok2 {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
if c := compareInt(pv.major, pw.major); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if c := compareInt(pv.minor, pw.minor); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if c := compareInt(pv.patch, pw.patch); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return comparePrerelease(pv.prerelease, pw.prerelease)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max canonicalizes its arguments and then returns the version string
|
|
||||||
// that compares greater.
|
|
||||||
//
|
|
||||||
// Deprecated: use [Compare] instead. In most cases, returning a canonicalized
|
|
||||||
// version is not expected or desired.
|
|
||||||
func Max(v, w string) string {
|
|
||||||
v = Canonical(v)
|
|
||||||
w = Canonical(w)
|
|
||||||
if Compare(v, w) > 0 {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByVersion implements [sort.Interface] for sorting semantic version strings.
|
|
||||||
type ByVersion []string
|
|
||||||
|
|
||||||
func (vs ByVersion) Len() int { return len(vs) }
|
|
||||||
func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
|
|
||||||
func (vs ByVersion) Less(i, j int) bool {
|
|
||||||
cmp := Compare(vs[i], vs[j])
|
|
||||||
if cmp != 0 {
|
|
||||||
return cmp < 0
|
|
||||||
}
|
|
||||||
return vs[i] < vs[j]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort sorts a list of semantic version strings using [ByVersion].
|
|
||||||
func Sort(list []string) {
|
|
||||||
sort.Sort(ByVersion(list))
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(v string) (p parsed, ok bool) {
|
|
||||||
if v == "" || v[0] != 'v' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.major, v, ok = parseInt(v[1:])
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v == "" {
|
|
||||||
p.minor = "0"
|
|
||||||
p.patch = "0"
|
|
||||||
p.short = ".0.0"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[0] != '.' {
|
|
||||||
ok = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.minor, v, ok = parseInt(v[1:])
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v == "" {
|
|
||||||
p.patch = "0"
|
|
||||||
p.short = ".0"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[0] != '.' {
|
|
||||||
ok = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.patch, v, ok = parseInt(v[1:])
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(v) > 0 && v[0] == '-' {
|
|
||||||
p.prerelease, v, ok = parsePrerelease(v)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(v) > 0 && v[0] == '+' {
|
|
||||||
p.build, v, ok = parseBuild(v)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if v != "" {
|
|
||||||
ok = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ok = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseInt(v string) (t, rest string, ok bool) {
|
|
||||||
if v == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[0] < '0' || '9' < v[0] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if v[0] == '0' && i != 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return v[:i], v[i:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
func parsePrerelease(v string) (t, rest string, ok bool) {
|
|
||||||
// "A pre-release version MAY be denoted by appending a hyphen and
|
|
||||||
// a series of dot separated identifiers immediately following the patch version.
|
|
||||||
// Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
|
|
||||||
// Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
|
|
||||||
if v == "" || v[0] != '-' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
start := 1
|
|
||||||
for i < len(v) && v[i] != '+' {
|
|
||||||
if !isIdentChar(v[i]) && v[i] != '.' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[i] == '.' {
|
|
||||||
if start == i || isBadNum(v[start:i]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
start = i + 1
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if start == i || isBadNum(v[start:i]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return v[:i], v[i:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBuild(v string) (t, rest string, ok bool) {
|
|
||||||
if v == "" || v[0] != '+' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
start := 1
|
|
||||||
for i < len(v) {
|
|
||||||
if !isIdentChar(v[i]) && v[i] != '.' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[i] == '.' {
|
|
||||||
if start == i {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
start = i + 1
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if start == i {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return v[:i], v[i:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIdentChar(c byte) bool {
|
|
||||||
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBadNum(v string) bool {
|
|
||||||
i := 0
|
|
||||||
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return i == len(v) && i > 1 && v[0] == '0'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNum(v string) bool {
|
|
||||||
i := 0
|
|
||||||
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return i == len(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareInt(x, y string) int {
|
|
||||||
if x == y {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if len(x) < len(y) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if len(x) > len(y) {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
if x < y {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func comparePrerelease(x, y string) int {
|
|
||||||
// "When major, minor, and patch are equal, a pre-release version has
|
|
||||||
// lower precedence than a normal version.
|
|
||||||
// Example: 1.0.0-alpha < 1.0.0.
|
|
||||||
// Precedence for two pre-release versions with the same major, minor,
|
|
||||||
// and patch version MUST be determined by comparing each dot separated
|
|
||||||
// identifier from left to right until a difference is found as follows:
|
|
||||||
// identifiers consisting of only digits are compared numerically and
|
|
||||||
// identifiers with letters or hyphens are compared lexically in ASCII
|
|
||||||
// sort order. Numeric identifiers always have lower precedence than
|
|
||||||
// non-numeric identifiers. A larger set of pre-release fields has a
|
|
||||||
// higher precedence than a smaller set, if all of the preceding
|
|
||||||
// identifiers are equal.
|
|
||||||
// Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
|
|
||||||
// 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
|
|
||||||
if x == y {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if x == "" {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
if y == "" {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
for x != "" && y != "" {
|
|
||||||
x = x[1:] // skip - or .
|
|
||||||
y = y[1:] // skip - or .
|
|
||||||
var dx, dy string
|
|
||||||
dx, x = nextIdent(x)
|
|
||||||
dy, y = nextIdent(y)
|
|
||||||
if dx != dy {
|
|
||||||
ix := isNum(dx)
|
|
||||||
iy := isNum(dy)
|
|
||||||
if ix != iy {
|
|
||||||
if ix {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ix {
|
|
||||||
if len(dx) < len(dy) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if len(dx) > len(dy) {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dx < dy {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if x == "" {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextIdent(x string) (dx, rest string) {
|
|
||||||
i := 0
|
|
||||||
for i < len(x) && x[i] != '.' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return x[:i], x[i:]
|
|
||||||
}
|
|
||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
Additional IP Rights Grant (Patents)
|
|
||||||
|
|
||||||
"This implementation" means the copyrightable works distributed by
|
|
||||||
Google as part of the Go project.
|
|
||||||
|
|
||||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
|
||||||
patent license to make, have made, use, offer to sell, sell, import,
|
|
||||||
transfer and otherwise run, modify and propagate the contents of this
|
|
||||||
implementation of Go, where such license applies only to those patent
|
|
||||||
claims, both currently owned or controlled by Google and acquired in
|
|
||||||
the future, licensable by Google that are necessarily infringed by this
|
|
||||||
implementation of Go. This grant does not include claims that would be
|
|
||||||
infringed only as a consequence of further modification of this
|
|
||||||
implementation. If you or your agent or exclusive licensee institute or
|
|
||||||
order or agree to the institution of patent litigation against any
|
|
||||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
|
||||||
that this implementation of Go or any code incorporated within this
|
|
||||||
implementation of Go constitutes direct or contributory patent
|
|
||||||
infringement, or inducement of patent infringement, then any patent
|
|
||||||
rights granted to you under this License for this implementation of Go
|
|
||||||
shall terminate as of the date such litigation is filed.
|
|
||||||
-660
@@ -1,660 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
|
|
||||||
// interface. Given the name of a (signed or unsigned) integer type T that has constants
|
|
||||||
// defined, stringer will create a new self-contained Go source file implementing
|
|
||||||
//
|
|
||||||
// func (t T) String() string
|
|
||||||
//
|
|
||||||
// The file is created in the same package and directory as the package that defines T.
|
|
||||||
// It has helpful defaults designed for use with go generate.
|
|
||||||
//
|
|
||||||
// Stringer works best with constants that are consecutive values such as created using iota,
|
|
||||||
// but creates good code regardless. In the future it might also provide custom support for
|
|
||||||
// constant sets that are bit patterns.
|
|
||||||
//
|
|
||||||
// For example, given this snippet,
|
|
||||||
//
|
|
||||||
// package painkiller
|
|
||||||
//
|
|
||||||
// type Pill int
|
|
||||||
//
|
|
||||||
// const (
|
|
||||||
// Placebo Pill = iota
|
|
||||||
// Aspirin
|
|
||||||
// Ibuprofen
|
|
||||||
// Paracetamol
|
|
||||||
// Acetaminophen = Paracetamol
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// running this command
|
|
||||||
//
|
|
||||||
// stringer -type=Pill
|
|
||||||
//
|
|
||||||
// in the same directory will create the file pill_string.go, in package painkiller,
|
|
||||||
// containing a definition of
|
|
||||||
//
|
|
||||||
// func (Pill) String() string
|
|
||||||
//
|
|
||||||
// That method will translate the value of a Pill constant to the string representation
|
|
||||||
// of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will
|
|
||||||
// print the string "Aspirin".
|
|
||||||
//
|
|
||||||
// Typically this process would be run using go generate, like this:
|
|
||||||
//
|
|
||||||
// //go:generate stringer -type=Pill
|
|
||||||
//
|
|
||||||
// If multiple constants have the same value, the lexically first matching name will
|
|
||||||
// be used (in the example, Acetaminophen will print as "Paracetamol").
|
|
||||||
//
|
|
||||||
// With no arguments, it processes the package in the current directory.
|
|
||||||
// Otherwise, the arguments must name a single directory holding a Go package
|
|
||||||
// or a set of Go source files that represent a single Go package.
|
|
||||||
//
|
|
||||||
// The -type flag accepts a comma-separated list of types so a single run can
|
|
||||||
// generate methods for multiple types. The default output file is t_string.go,
|
|
||||||
// where t is the lower-cased name of the first type listed. It can be overridden
|
|
||||||
// with the -output flag.
|
|
||||||
//
|
|
||||||
// The -linecomment flag tells stringer to generate the text of any line comment, trimmed
|
|
||||||
// of leading spaces, instead of the constant name. For instance, if the constants above had a
|
|
||||||
// Pill prefix, one could write
|
|
||||||
//
|
|
||||||
// PillAspirin // Aspirin
|
|
||||||
//
|
|
||||||
// to suppress it in the output.
|
|
||||||
package main // import "golang.org/x/tools/cmd/stringer"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"go/ast"
|
|
||||||
"go/constant"
|
|
||||||
"go/format"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/go/packages"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
|
|
||||||
output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
|
|
||||||
trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names")
|
|
||||||
linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present")
|
|
||||||
buildTags = flag.String("tags", "", "comma-separated list of build tags to apply")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Usage is a replacement usage function for the flags package.
|
|
||||||
func Usage() {
|
|
||||||
fmt.Fprintf(os.Stderr, "Usage of stringer:\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T files... # Must be a single package\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "For more information, see:\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "\thttps://pkg.go.dev/golang.org/x/tools/cmd/stringer\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "Flags:\n")
|
|
||||||
flag.PrintDefaults()
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.SetFlags(0)
|
|
||||||
log.SetPrefix("stringer: ")
|
|
||||||
flag.Usage = Usage
|
|
||||||
flag.Parse()
|
|
||||||
if len(*typeNames) == 0 {
|
|
||||||
flag.Usage()
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
types := strings.Split(*typeNames, ",")
|
|
||||||
var tags []string
|
|
||||||
if len(*buildTags) > 0 {
|
|
||||||
tags = strings.Split(*buildTags, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// We accept either one directory or a list of files. Which do we have?
|
|
||||||
args := flag.Args()
|
|
||||||
if len(args) == 0 {
|
|
||||||
// Default: process whole package in current directory.
|
|
||||||
args = []string{"."}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the package once.
|
|
||||||
var dir string
|
|
||||||
g := Generator{
|
|
||||||
trimPrefix: *trimprefix,
|
|
||||||
lineComment: *linecomment,
|
|
||||||
}
|
|
||||||
// TODO(suzmue): accept other patterns for packages (directories, list of files, import paths, etc).
|
|
||||||
if len(args) == 1 && isDirectory(args[0]) {
|
|
||||||
dir = args[0]
|
|
||||||
} else {
|
|
||||||
if len(tags) != 0 {
|
|
||||||
log.Fatal("-tags option applies only to directories, not when files are specified")
|
|
||||||
}
|
|
||||||
dir = filepath.Dir(args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
g.parsePackage(args, tags)
|
|
||||||
|
|
||||||
// Print the header and package clause.
|
|
||||||
g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))
|
|
||||||
g.Printf("\n")
|
|
||||||
g.Printf("package %s", g.pkg.name)
|
|
||||||
g.Printf("\n")
|
|
||||||
g.Printf("import \"strconv\"\n") // Used by all methods.
|
|
||||||
|
|
||||||
// Run generate for each type.
|
|
||||||
for _, typeName := range types {
|
|
||||||
g.generate(typeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the output.
|
|
||||||
src := g.format()
|
|
||||||
|
|
||||||
// Write to file.
|
|
||||||
outputName := *output
|
|
||||||
if outputName == "" {
|
|
||||||
baseName := fmt.Sprintf("%s_string.go", types[0])
|
|
||||||
outputName = filepath.Join(dir, strings.ToLower(baseName))
|
|
||||||
}
|
|
||||||
err := os.WriteFile(outputName, src, 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("writing output: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isDirectory reports whether the named file is a directory.
|
|
||||||
func isDirectory(name string) bool {
|
|
||||||
info, err := os.Stat(name)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
return info.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generator holds the state of the analysis. Primarily used to buffer
|
|
||||||
// the output for format.Source.
|
|
||||||
type Generator struct {
|
|
||||||
buf bytes.Buffer // Accumulated output.
|
|
||||||
pkg *Package // Package we are scanning.
|
|
||||||
|
|
||||||
trimPrefix string
|
|
||||||
lineComment bool
|
|
||||||
|
|
||||||
logf func(format string, args ...interface{}) // test logging hook; nil when not testing
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Generator) Printf(format string, args ...interface{}) {
|
|
||||||
fmt.Fprintf(&g.buf, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// File holds a single parsed file and associated data.
|
|
||||||
type File struct {
|
|
||||||
pkg *Package // Package to which this file belongs.
|
|
||||||
file *ast.File // Parsed AST.
|
|
||||||
// These fields are reset for each type being generated.
|
|
||||||
typeName string // Name of the constant type.
|
|
||||||
values []Value // Accumulator for constant values of that type.
|
|
||||||
|
|
||||||
trimPrefix string
|
|
||||||
lineComment bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Package struct {
|
|
||||||
name string
|
|
||||||
defs map[*ast.Ident]types.Object
|
|
||||||
files []*File
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsePackage analyzes the single package constructed from the patterns and tags.
|
|
||||||
// parsePackage exits if there is an error.
|
|
||||||
func (g *Generator) parsePackage(patterns []string, tags []string) {
|
|
||||||
cfg := &packages.Config{
|
|
||||||
Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,
|
|
||||||
// TODO: Need to think about constants in test files. Maybe write type_string_test.go
|
|
||||||
// in a separate pass? For later.
|
|
||||||
Tests: false,
|
|
||||||
BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))},
|
|
||||||
Logf: g.logf,
|
|
||||||
}
|
|
||||||
pkgs, err := packages.Load(cfg, patterns...)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(pkgs) != 1 {
|
|
||||||
log.Fatalf("error: %d packages matching %v", len(pkgs), strings.Join(patterns, " "))
|
|
||||||
}
|
|
||||||
g.addPackage(pkgs[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// addPackage adds a type checked Package and its syntax files to the generator.
|
|
||||||
func (g *Generator) addPackage(pkg *packages.Package) {
|
|
||||||
g.pkg = &Package{
|
|
||||||
name: pkg.Name,
|
|
||||||
defs: pkg.TypesInfo.Defs,
|
|
||||||
files: make([]*File, len(pkg.Syntax)),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, file := range pkg.Syntax {
|
|
||||||
g.pkg.files[i] = &File{
|
|
||||||
file: file,
|
|
||||||
pkg: g.pkg,
|
|
||||||
trimPrefix: g.trimPrefix,
|
|
||||||
lineComment: g.lineComment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate produces the String method for the named type.
|
|
||||||
func (g *Generator) generate(typeName string) {
|
|
||||||
values := make([]Value, 0, 100)
|
|
||||||
for _, file := range g.pkg.files {
|
|
||||||
// Set the state for this run of the walker.
|
|
||||||
file.typeName = typeName
|
|
||||||
file.values = nil
|
|
||||||
if file.file != nil {
|
|
||||||
ast.Inspect(file.file, file.genDecl)
|
|
||||||
values = append(values, file.values...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(values) == 0 {
|
|
||||||
log.Fatalf("no values defined for type %s", typeName)
|
|
||||||
}
|
|
||||||
// Generate code that will fail if the constants change value.
|
|
||||||
g.Printf("func _() {\n")
|
|
||||||
g.Printf("\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n")
|
|
||||||
g.Printf("\t// Re-run the stringer command to generate them again.\n")
|
|
||||||
g.Printf("\tvar x [1]struct{}\n")
|
|
||||||
for _, v := range values {
|
|
||||||
g.Printf("\t_ = x[%s - %s]\n", v.originalName, v.str)
|
|
||||||
}
|
|
||||||
g.Printf("}\n")
|
|
||||||
runs := splitIntoRuns(values)
|
|
||||||
// The decision of which pattern to use depends on the number of
|
|
||||||
// runs in the numbers. If there's only one, it's easy. For more than
|
|
||||||
// one, there's a tradeoff between complexity and size of the data
|
|
||||||
// and code vs. the simplicity of a map. A map takes more space,
|
|
||||||
// but so does the code. The decision here (crossover at 10) is
|
|
||||||
// arbitrary, but considers that for large numbers of runs the cost
|
|
||||||
// of the linear scan in the switch might become important, and
|
|
||||||
// rather than use yet another algorithm such as binary search,
|
|
||||||
// we punt and use a map. In any case, the likelihood of a map
|
|
||||||
// being necessary for any realistic example other than bitmasks
|
|
||||||
// is very low. And bitmasks probably deserve their own analysis,
|
|
||||||
// to be done some other day.
|
|
||||||
switch {
|
|
||||||
case len(runs) == 1:
|
|
||||||
g.buildOneRun(runs, typeName)
|
|
||||||
case len(runs) <= 10:
|
|
||||||
g.buildMultipleRuns(runs, typeName)
|
|
||||||
default:
|
|
||||||
g.buildMap(runs, typeName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// splitIntoRuns breaks the values into runs of contiguous sequences.
|
|
||||||
// For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}.
|
|
||||||
// The input slice is known to be non-empty.
|
|
||||||
func splitIntoRuns(values []Value) [][]Value {
|
|
||||||
// We use stable sort so the lexically first name is chosen for equal elements.
|
|
||||||
sort.Stable(byValue(values))
|
|
||||||
// Remove duplicates. Stable sort has put the one we want to print first,
|
|
||||||
// so use that one. The String method won't care about which named constant
|
|
||||||
// was the argument, so the first name for the given value is the only one to keep.
|
|
||||||
// We need to do this because identical values would cause the switch or map
|
|
||||||
// to fail to compile.
|
|
||||||
j := 1
|
|
||||||
for i := 1; i < len(values); i++ {
|
|
||||||
if values[i].value != values[i-1].value {
|
|
||||||
values[j] = values[i]
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
values = values[:j]
|
|
||||||
runs := make([][]Value, 0, 10)
|
|
||||||
for len(values) > 0 {
|
|
||||||
// One contiguous sequence per outer loop.
|
|
||||||
i := 1
|
|
||||||
for i < len(values) && values[i].value == values[i-1].value+1 {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
runs = append(runs, values[:i])
|
|
||||||
values = values[i:]
|
|
||||||
}
|
|
||||||
return runs
|
|
||||||
}
|
|
||||||
|
|
||||||
// format returns the gofmt-ed contents of the Generator's buffer.
|
|
||||||
func (g *Generator) format() []byte {
|
|
||||||
src, err := format.Source(g.buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
// Should never happen, but can arise when developing this code.
|
|
||||||
// The user can compile the output to see the error.
|
|
||||||
log.Printf("warning: internal error: invalid Go generated: %s", err)
|
|
||||||
log.Printf("warning: compile the package to analyze the error")
|
|
||||||
return g.buf.Bytes()
|
|
||||||
}
|
|
||||||
return src
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value represents a declared constant.
|
|
||||||
type Value struct {
|
|
||||||
originalName string // The name of the constant.
|
|
||||||
name string // The name with trimmed prefix.
|
|
||||||
// The value is stored as a bit pattern alone. The boolean tells us
|
|
||||||
// whether to interpret it as an int64 or a uint64; the only place
|
|
||||||
// this matters is when sorting.
|
|
||||||
// Much of the time the str field is all we need; it is printed
|
|
||||||
// by Value.String.
|
|
||||||
value uint64 // Will be converted to int64 when needed.
|
|
||||||
signed bool // Whether the constant is a signed type.
|
|
||||||
str string // The string representation given by the "go/constant" package.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Value) String() string {
|
|
||||||
return v.str
|
|
||||||
}
|
|
||||||
|
|
||||||
// byValue lets us sort the constants into increasing order.
|
|
||||||
// We take care in the Less method to sort in signed or unsigned order,
|
|
||||||
// as appropriate.
|
|
||||||
type byValue []Value
|
|
||||||
|
|
||||||
func (b byValue) Len() int { return len(b) }
|
|
||||||
func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
|
||||||
func (b byValue) Less(i, j int) bool {
|
|
||||||
if b[i].signed {
|
|
||||||
return int64(b[i].value) < int64(b[j].value)
|
|
||||||
}
|
|
||||||
return b[i].value < b[j].value
|
|
||||||
}
|
|
||||||
|
|
||||||
// genDecl processes one declaration clause.
|
|
||||||
func (f *File) genDecl(node ast.Node) bool {
|
|
||||||
decl, ok := node.(*ast.GenDecl)
|
|
||||||
if !ok || decl.Tok != token.CONST {
|
|
||||||
// We only care about const declarations.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// The name of the type of the constants we are declaring.
|
|
||||||
// Can change if this is a multi-element declaration.
|
|
||||||
typ := ""
|
|
||||||
// Loop over the elements of the declaration. Each element is a ValueSpec:
|
|
||||||
// a list of names possibly followed by a type, possibly followed by values.
|
|
||||||
// If the type and value are both missing, we carry down the type (and value,
|
|
||||||
// but the "go/types" package takes care of that).
|
|
||||||
for _, spec := range decl.Specs {
|
|
||||||
vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST.
|
|
||||||
if vspec.Type == nil && len(vspec.Values) > 0 {
|
|
||||||
// "X = 1". With no type but a value. If the constant is untyped,
|
|
||||||
// skip this vspec and reset the remembered type.
|
|
||||||
typ = ""
|
|
||||||
|
|
||||||
// If this is a simple type conversion, remember the type.
|
|
||||||
// We don't mind if this is actually a call; a qualified call won't
|
|
||||||
// be matched (that will be SelectorExpr, not Ident), and only unusual
|
|
||||||
// situations will result in a function call that appears to be
|
|
||||||
// a type conversion.
|
|
||||||
ce, ok := vspec.Values[0].(*ast.CallExpr)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
id, ok := ce.Fun.(*ast.Ident)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
typ = id.Name
|
|
||||||
}
|
|
||||||
if vspec.Type != nil {
|
|
||||||
// "X T". We have a type. Remember it.
|
|
||||||
ident, ok := vspec.Type.(*ast.Ident)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
typ = ident.Name
|
|
||||||
}
|
|
||||||
if typ != f.typeName {
|
|
||||||
// This is not the type we're looking for.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// We now have a list of names (from one line of source code) all being
|
|
||||||
// declared with the desired type.
|
|
||||||
// Grab their names and actual values and store them in f.values.
|
|
||||||
for _, name := range vspec.Names {
|
|
||||||
if name.Name == "_" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// This dance lets the type checker find the values for us. It's a
|
|
||||||
// bit tricky: look up the object declared by the name, find its
|
|
||||||
// types.Const, and extract its value.
|
|
||||||
obj, ok := f.pkg.defs[name]
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("no value for constant %s", name)
|
|
||||||
}
|
|
||||||
info := obj.Type().Underlying().(*types.Basic).Info()
|
|
||||||
if info&types.IsInteger == 0 {
|
|
||||||
log.Fatalf("can't handle non-integer constant type %s", typ)
|
|
||||||
}
|
|
||||||
value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST.
|
|
||||||
if value.Kind() != constant.Int {
|
|
||||||
log.Fatalf("can't happen: constant is not an integer %s", name)
|
|
||||||
}
|
|
||||||
i64, isInt := constant.Int64Val(value)
|
|
||||||
u64, isUint := constant.Uint64Val(value)
|
|
||||||
if !isInt && !isUint {
|
|
||||||
log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String())
|
|
||||||
}
|
|
||||||
if !isInt {
|
|
||||||
u64 = uint64(i64)
|
|
||||||
}
|
|
||||||
v := Value{
|
|
||||||
originalName: name.Name,
|
|
||||||
value: u64,
|
|
||||||
signed: info&types.IsUnsigned == 0,
|
|
||||||
str: value.String(),
|
|
||||||
}
|
|
||||||
if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 {
|
|
||||||
v.name = strings.TrimSpace(c.Text())
|
|
||||||
} else {
|
|
||||||
v.name = strings.TrimPrefix(v.originalName, f.trimPrefix)
|
|
||||||
}
|
|
||||||
f.values = append(f.values, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
|
|
||||||
// usize returns the number of bits of the smallest unsigned integer
|
|
||||||
// type that will hold n. Used to create the smallest possible slice of
|
|
||||||
// integers to use as indexes into the concatenated strings.
|
|
||||||
func usize(n int) int {
|
|
||||||
switch {
|
|
||||||
case n < 1<<8:
|
|
||||||
return 8
|
|
||||||
case n < 1<<16:
|
|
||||||
return 16
|
|
||||||
default:
|
|
||||||
// 2^32 is enough constants for anyone.
|
|
||||||
return 32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// declareIndexAndNameVars declares the index slices and concatenated names
|
|
||||||
// strings representing the runs of values.
|
|
||||||
func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
|
|
||||||
var indexes, names []string
|
|
||||||
for i, run := range runs {
|
|
||||||
index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
|
|
||||||
if len(run) != 1 {
|
|
||||||
indexes = append(indexes, index)
|
|
||||||
}
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
g.Printf("const (\n")
|
|
||||||
for _, name := range names {
|
|
||||||
g.Printf("\t%s\n", name)
|
|
||||||
}
|
|
||||||
g.Printf(")\n\n")
|
|
||||||
|
|
||||||
if len(indexes) > 0 {
|
|
||||||
g.Printf("var (")
|
|
||||||
for _, index := range indexes {
|
|
||||||
g.Printf("\t%s\n", index)
|
|
||||||
}
|
|
||||||
g.Printf(")\n\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
|
|
||||||
func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) {
|
|
||||||
index, name := g.createIndexAndNameDecl(run, typeName, "")
|
|
||||||
g.Printf("const %s\n", name)
|
|
||||||
g.Printf("var %s\n", index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var".
|
|
||||||
func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) {
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
indexes := make([]int, len(run))
|
|
||||||
for i := range run {
|
|
||||||
b.WriteString(run[i].name)
|
|
||||||
indexes[i] = b.Len()
|
|
||||||
}
|
|
||||||
nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String())
|
|
||||||
nameLen := b.Len()
|
|
||||||
b.Reset()
|
|
||||||
fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen))
|
|
||||||
for i, v := range indexes {
|
|
||||||
if i > 0 {
|
|
||||||
fmt.Fprintf(b, ", ")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "%d", v)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "}")
|
|
||||||
return b.String(), nameConst
|
|
||||||
}
|
|
||||||
|
|
||||||
// declareNameVars declares the concatenated names string representing all the values in the runs.
|
|
||||||
func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) {
|
|
||||||
g.Printf("const _%s_name%s = \"", typeName, suffix)
|
|
||||||
for _, run := range runs {
|
|
||||||
for i := range run {
|
|
||||||
g.Printf("%s", run[i].name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.Printf("\"\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildOneRun generates the variables and String method for a single run of contiguous values.
|
|
||||||
func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
|
|
||||||
values := runs[0]
|
|
||||||
g.Printf("\n")
|
|
||||||
g.declareIndexAndNameVar(values, typeName)
|
|
||||||
// The generated code is simple enough to write as a Printf format.
|
|
||||||
lessThanZero := ""
|
|
||||||
if values[0].signed {
|
|
||||||
lessThanZero = "i < 0 || "
|
|
||||||
}
|
|
||||||
if values[0].value == 0 { // Signed or unsigned, 0 is still 0.
|
|
||||||
g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero)
|
|
||||||
} else {
|
|
||||||
g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arguments to format are:
|
|
||||||
//
|
|
||||||
// [1]: type name
|
|
||||||
// [2]: size of index element (8 for uint8 etc.)
|
|
||||||
// [3]: less than zero check (for signed types)
|
|
||||||
const stringOneRun = `func (i %[1]s) String() string {
|
|
||||||
if %[3]si >= %[1]s(len(_%[1]s_index)-1) {
|
|
||||||
return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
// Arguments to format are:
|
|
||||||
// [1]: type name
|
|
||||||
// [2]: lowest defined value for type, as a string
|
|
||||||
// [3]: size of index element (8 for uint8 etc.)
|
|
||||||
// [4]: less than zero check (for signed types)
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
const stringOneRunWithOffset = `func (i %[1]s) String() string {
|
|
||||||
i -= %[2]s
|
|
||||||
if %[4]si >= %[1]s(len(_%[1]s_index)-1) {
|
|
||||||
return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")"
|
|
||||||
}
|
|
||||||
return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
// buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
|
|
||||||
// For this pattern, a single Printf format won't do.
|
|
||||||
func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {
|
|
||||||
g.Printf("\n")
|
|
||||||
g.declareIndexAndNameVars(runs, typeName)
|
|
||||||
g.Printf("func (i %s) String() string {\n", typeName)
|
|
||||||
g.Printf("\tswitch {\n")
|
|
||||||
for i, values := range runs {
|
|
||||||
if len(values) == 1 {
|
|
||||||
g.Printf("\tcase i == %s:\n", &values[0])
|
|
||||||
g.Printf("\t\treturn _%s_name_%d\n", typeName, i)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if values[0].value == 0 && !values[0].signed {
|
|
||||||
// For an unsigned lower bound of 0, "0 <= i" would be redundant.
|
|
||||||
g.Printf("\tcase i <= %s:\n", &values[len(values)-1])
|
|
||||||
} else {
|
|
||||||
g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1])
|
|
||||||
}
|
|
||||||
if values[0].value != 0 {
|
|
||||||
g.Printf("\t\ti -= %s\n", &values[0])
|
|
||||||
}
|
|
||||||
g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n",
|
|
||||||
typeName, i, typeName, i, typeName, i)
|
|
||||||
}
|
|
||||||
g.Printf("\tdefault:\n")
|
|
||||||
g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName)
|
|
||||||
g.Printf("\t}\n")
|
|
||||||
g.Printf("}\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildMap handles the case where the space is so sparse a map is a reasonable fallback.
|
|
||||||
// It's a rare situation but has simple code.
|
|
||||||
func (g *Generator) buildMap(runs [][]Value, typeName string) {
|
|
||||||
g.Printf("\n")
|
|
||||||
g.declareNameVars(runs, typeName, "")
|
|
||||||
g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName)
|
|
||||||
n := 0
|
|
||||||
for _, values := range runs {
|
|
||||||
for _, value := range values {
|
|
||||||
g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name))
|
|
||||||
n += len(value.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.Printf("}\n\n")
|
|
||||||
g.Printf(stringMap, typeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Argument to format is the type name.
|
|
||||||
const stringMap = `func (i %[1]s) String() string {
|
|
||||||
if str, ok := _%[1]s_map[i]; ok {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
`
|
|
||||||
-186
@@ -1,186 +0,0 @@
|
|||||||
// Copyright 2016 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.
|
|
||||||
|
|
||||||
// Package gcexportdata provides functions for locating, reading, and
|
|
||||||
// writing export data files containing type information produced by the
|
|
||||||
// gc compiler. This package supports go1.7 export data format and all
|
|
||||||
// later versions.
|
|
||||||
//
|
|
||||||
// Although it might seem convenient for this package to live alongside
|
|
||||||
// go/types in the standard library, this would cause version skew
|
|
||||||
// problems for developer tools that use it, since they must be able to
|
|
||||||
// consume the outputs of the gc compiler both before and after a Go
|
|
||||||
// update such as from Go 1.7 to Go 1.8. Because this package lives in
|
|
||||||
// golang.org/x/tools, sites can update their version of this repo some
|
|
||||||
// time before the Go 1.8 release and rebuild and redeploy their
|
|
||||||
// developer tools, which will then be able to consume both Go 1.7 and
|
|
||||||
// Go 1.8 export data files, so they will work before and after the
|
|
||||||
// Go update. (See discussion at https://golang.org/issue/15651.)
|
|
||||||
package gcexportdata // import "golang.org/x/tools/go/gcexportdata"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"io"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/gcimporter"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Find returns the name of an object (.o) or archive (.a) file
|
|
||||||
// containing type information for the specified import path,
|
|
||||||
// using the go command.
|
|
||||||
// If no file was found, an empty filename is returned.
|
|
||||||
//
|
|
||||||
// A relative srcDir is interpreted relative to the current working directory.
|
|
||||||
//
|
|
||||||
// Find also returns the package's resolved (canonical) import path,
|
|
||||||
// reflecting the effects of srcDir and vendoring on importPath.
|
|
||||||
//
|
|
||||||
// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
|
|
||||||
// which is more efficient.
|
|
||||||
func Find(importPath, srcDir string) (filename, path string) {
|
|
||||||
cmd := exec.Command("go", "list", "-json", "-export", "--", importPath)
|
|
||||||
cmd.Dir = srcDir
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
var data struct {
|
|
||||||
ImportPath string
|
|
||||||
Export string
|
|
||||||
}
|
|
||||||
json.Unmarshal(out, &data)
|
|
||||||
return data.Export, data.ImportPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewReader returns a reader for the export data section of an object
|
|
||||||
// (.o) or archive (.a) file read from r. The new reader may provide
|
|
||||||
// additional trailing data beyond the end of the export data.
|
|
||||||
func NewReader(r io.Reader) (io.Reader, error) {
|
|
||||||
buf := bufio.NewReader(r)
|
|
||||||
_, size, err := gcimporter.FindExportData(buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if size >= 0 {
|
|
||||||
// We were given an archive and found the __.PKGDEF in it.
|
|
||||||
// This tells us the size of the export data, and we don't
|
|
||||||
// need to return the entire file.
|
|
||||||
return &io.LimitedReader{
|
|
||||||
R: buf,
|
|
||||||
N: size,
|
|
||||||
}, nil
|
|
||||||
} else {
|
|
||||||
// We were given an object file. As such, we don't know how large
|
|
||||||
// the export data is and must return the entire file.
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// readAll works the same way as io.ReadAll, but avoids allocations and copies
|
|
||||||
// by preallocating a byte slice of the necessary size if the size is known up
|
|
||||||
// front. This is always possible when the input is an archive. In that case,
|
|
||||||
// NewReader will return the known size using an io.LimitedReader.
|
|
||||||
func readAll(r io.Reader) ([]byte, error) {
|
|
||||||
if lr, ok := r.(*io.LimitedReader); ok {
|
|
||||||
data := make([]byte, lr.N)
|
|
||||||
_, err := io.ReadFull(lr, data)
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
return io.ReadAll(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads export data from in, decodes it, and returns type
|
|
||||||
// information for the package.
|
|
||||||
//
|
|
||||||
// The package path (effectively its linker symbol prefix) is
|
|
||||||
// specified by path, since unlike the package name, this information
|
|
||||||
// may not be recorded in the export data.
|
|
||||||
//
|
|
||||||
// File position information is added to fset.
|
|
||||||
//
|
|
||||||
// Read may inspect and add to the imports map to ensure that references
|
|
||||||
// within the export data to other packages are consistent. The caller
|
|
||||||
// must ensure that imports[path] does not exist, or exists but is
|
|
||||||
// incomplete (see types.Package.Complete), and Read inserts the
|
|
||||||
// resulting package into this map entry.
|
|
||||||
//
|
|
||||||
// On return, the state of the reader is undefined.
|
|
||||||
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
|
|
||||||
data, err := readAll(in)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading export data for %q: %v", path, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bytes.HasPrefix(data, []byte("!<arch>")) {
|
|
||||||
return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The indexed export format starts with an 'i'; the older
|
|
||||||
// binary export format starts with a 'c', 'd', or 'v'
|
|
||||||
// (from "version"). Select appropriate importer.
|
|
||||||
if len(data) > 0 {
|
|
||||||
switch data[0] {
|
|
||||||
case 'v', 'c', 'd': // binary, till go1.10
|
|
||||||
return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0])
|
|
||||||
|
|
||||||
case 'i': // indexed, till go1.19
|
|
||||||
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
|
|
||||||
return pkg, err
|
|
||||||
|
|
||||||
case 'u': // unified, from go1.20
|
|
||||||
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
|
|
||||||
return pkg, err
|
|
||||||
|
|
||||||
default:
|
|
||||||
l := len(data)
|
|
||||||
if l > 10 {
|
|
||||||
l = 10
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("empty export data for %s", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes encoded type information for the specified package to out.
|
|
||||||
// The FileSet provides file position information for named objects.
|
|
||||||
func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
|
|
||||||
if _, err := io.WriteString(out, "i"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return gcimporter.IExportData(out, fset, pkg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadBundle reads an export bundle from in, decodes it, and returns type
|
|
||||||
// information for the packages.
|
|
||||||
// File position information is added to fset.
|
|
||||||
//
|
|
||||||
// ReadBundle may inspect and add to the imports map to ensure that references
|
|
||||||
// within the export bundle to other packages are consistent.
|
|
||||||
//
|
|
||||||
// On return, the state of the reader is undefined.
|
|
||||||
//
|
|
||||||
// Experimental: This API is experimental and may change in the future.
|
|
||||||
func ReadBundle(in io.Reader, fset *token.FileSet, imports map[string]*types.Package) ([]*types.Package, error) {
|
|
||||||
data, err := readAll(in)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading export bundle: %v", err)
|
|
||||||
}
|
|
||||||
return gcimporter.IImportBundle(fset, imports, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteBundle writes encoded type information for the specified packages to out.
|
|
||||||
// The FileSet provides file position information for named objects.
|
|
||||||
//
|
|
||||||
// Experimental: This API is experimental and may change in the future.
|
|
||||||
func WriteBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error {
|
|
||||||
return gcimporter.IExportBundle(out, fset, pkgs)
|
|
||||||
}
|
|
||||||
-75
@@ -1,75 +0,0 @@
|
|||||||
// Copyright 2016 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.
|
|
||||||
|
|
||||||
package gcexportdata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewImporter returns a new instance of the types.Importer interface
|
|
||||||
// that reads type information from export data files written by gc.
|
|
||||||
// The Importer also satisfies types.ImporterFrom.
|
|
||||||
//
|
|
||||||
// Export data files are located using "go build" workspace conventions
|
|
||||||
// and the build.Default context.
|
|
||||||
//
|
|
||||||
// Use this importer instead of go/importer.For("gc", ...) to avoid the
|
|
||||||
// version-skew problems described in the documentation of this package,
|
|
||||||
// or to control the FileSet or access the imports map populated during
|
|
||||||
// package loading.
|
|
||||||
//
|
|
||||||
// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
|
|
||||||
// which is more efficient.
|
|
||||||
func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
|
|
||||||
return importer{fset, imports}
|
|
||||||
}
|
|
||||||
|
|
||||||
type importer struct {
|
|
||||||
fset *token.FileSet
|
|
||||||
imports map[string]*types.Package
|
|
||||||
}
|
|
||||||
|
|
||||||
func (imp importer) Import(importPath string) (*types.Package, error) {
|
|
||||||
return imp.ImportFrom(importPath, "", 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) {
|
|
||||||
filename, path := Find(importPath, srcDir)
|
|
||||||
if filename == "" {
|
|
||||||
if importPath == "unsafe" {
|
|
||||||
// Even for unsafe, call Find first in case
|
|
||||||
// the package was vendored.
|
|
||||||
return types.Unsafe, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("can't find import: %s", importPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pkg, ok := imp.imports[path]; ok && pkg.Complete() {
|
|
||||||
return pkg, nil // cache hit
|
|
||||||
}
|
|
||||||
|
|
||||||
// open file
|
|
||||||
f, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
f.Close()
|
|
||||||
if err != nil {
|
|
||||||
// add file name to error
|
|
||||||
err = fmt.Errorf("reading export data: %s: %v", filename, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
r, err := NewReader(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return Read(r, imp.fset, imp.imports, path)
|
|
||||||
}
|
|
||||||
-53
@@ -1,53 +0,0 @@
|
|||||||
// Copyright 2018 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.
|
|
||||||
|
|
||||||
// Package packagesdriver fetches type sizes for go/packages and go/analysis.
|
|
||||||
package packagesdriver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/gocommand"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) {
|
|
||||||
inv.Verb = "list"
|
|
||||||
inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}
|
|
||||||
stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
|
|
||||||
var goarch, compiler string
|
|
||||||
if rawErr != nil {
|
|
||||||
rawErrMsg := rawErr.Error()
|
|
||||||
if strings.Contains(rawErrMsg, "cannot find main module") ||
|
|
||||||
strings.Contains(rawErrMsg, "go.mod file not found") {
|
|
||||||
// User's running outside of a module.
|
|
||||||
// All bets are off. Get GOARCH and guess compiler is gc.
|
|
||||||
// TODO(matloob): Is this a problem in practice?
|
|
||||||
inv.Verb = "env"
|
|
||||||
inv.Args = []string{"GOARCH"}
|
|
||||||
envout, enverr := gocmdRunner.Run(ctx, inv)
|
|
||||||
if enverr != nil {
|
|
||||||
return "", "", enverr
|
|
||||||
}
|
|
||||||
goarch = strings.TrimSpace(envout.String())
|
|
||||||
compiler = "gc"
|
|
||||||
} else if friendlyErr != nil {
|
|
||||||
return "", "", friendlyErr
|
|
||||||
} else {
|
|
||||||
// This should be unreachable, but be defensive
|
|
||||||
// in case RunRaw's error results are inconsistent.
|
|
||||||
return "", "", rawErr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fields := strings.Fields(stdout.String())
|
|
||||||
if len(fields) < 2 {
|
|
||||||
return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>",
|
|
||||||
stdout.String(), stderr.String())
|
|
||||||
}
|
|
||||||
goarch = fields[0]
|
|
||||||
compiler = fields[1]
|
|
||||||
}
|
|
||||||
return compiler, goarch, nil
|
|
||||||
}
|
|
||||||
-220
@@ -1,220 +0,0 @@
|
|||||||
// Copyright 2018 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.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package packages loads Go packages for inspection and analysis.
|
|
||||||
|
|
||||||
The Load function takes as input a list of patterns and return a list of Package
|
|
||||||
structs describing individual packages matched by those patterns.
|
|
||||||
The LoadMode controls the amount of detail in the loaded packages.
|
|
||||||
|
|
||||||
Load passes most patterns directly to the underlying build tool,
|
|
||||||
but all patterns with the prefix "query=", where query is a
|
|
||||||
non-empty string of letters from [a-z], are reserved and may be
|
|
||||||
interpreted as query operators.
|
|
||||||
|
|
||||||
Two query operators are currently supported: "file" and "pattern".
|
|
||||||
|
|
||||||
The query "file=path/to/file.go" matches the package or packages enclosing
|
|
||||||
the Go source file path/to/file.go. For example "file=~/go/src/fmt/print.go"
|
|
||||||
might return the packages "fmt" and "fmt [fmt.test]".
|
|
||||||
|
|
||||||
The query "pattern=string" causes "string" to be passed directly to
|
|
||||||
the underlying build tool. In most cases this is unnecessary,
|
|
||||||
but an application can use Load("pattern=" + x) as an escaping mechanism
|
|
||||||
to ensure that x is not interpreted as a query operator if it contains '='.
|
|
||||||
|
|
||||||
All other query operators are reserved for future use and currently
|
|
||||||
cause Load to report an error.
|
|
||||||
|
|
||||||
The Package struct provides basic information about the package, including
|
|
||||||
|
|
||||||
- ID, a unique identifier for the package in the returned set;
|
|
||||||
- GoFiles, the names of the package's Go source files;
|
|
||||||
- Imports, a map from source import strings to the Packages they name;
|
|
||||||
- Types, the type information for the package's exported symbols;
|
|
||||||
- Syntax, the parsed syntax trees for the package's source code; and
|
|
||||||
- TypesInfo, the result of a complete type-check of the package syntax trees.
|
|
||||||
|
|
||||||
(See the documentation for type Package for the complete list of fields
|
|
||||||
and more detailed descriptions.)
|
|
||||||
|
|
||||||
For example,
|
|
||||||
|
|
||||||
Load(nil, "bytes", "unicode...")
|
|
||||||
|
|
||||||
returns four Package structs describing the standard library packages
|
|
||||||
bytes, unicode, unicode/utf16, and unicode/utf8. Note that one pattern
|
|
||||||
can match multiple packages and that a package might be matched by
|
|
||||||
multiple patterns: in general it is not possible to determine which
|
|
||||||
packages correspond to which patterns.
|
|
||||||
|
|
||||||
Note that the list returned by Load contains only the packages matched
|
|
||||||
by the patterns. Their dependencies can be found by walking the import
|
|
||||||
graph using the Imports fields.
|
|
||||||
|
|
||||||
The Load function can be configured by passing a pointer to a Config as
|
|
||||||
the first argument. A nil Config is equivalent to the zero Config, which
|
|
||||||
causes Load to run in LoadFiles mode, collecting minimal information.
|
|
||||||
See the documentation for type Config for details.
|
|
||||||
|
|
||||||
As noted earlier, the Config.Mode controls the amount of detail
|
|
||||||
reported about the loaded packages. See the documentation for type LoadMode
|
|
||||||
for details.
|
|
||||||
|
|
||||||
Most tools should pass their command-line arguments (after any flags)
|
|
||||||
uninterpreted to the loader, so that the loader can interpret them
|
|
||||||
according to the conventions of the underlying build system.
|
|
||||||
See the Example function for typical usage.
|
|
||||||
*/
|
|
||||||
package packages // import "golang.org/x/tools/go/packages"
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Motivation and design considerations
|
|
||||||
|
|
||||||
The new package's design solves problems addressed by two existing
|
|
||||||
packages: go/build, which locates and describes packages, and
|
|
||||||
golang.org/x/tools/go/loader, which loads, parses and type-checks them.
|
|
||||||
The go/build.Package structure encodes too much of the 'go build' way
|
|
||||||
of organizing projects, leaving us in need of a data type that describes a
|
|
||||||
package of Go source code independent of the underlying build system.
|
|
||||||
We wanted something that works equally well with go build and vgo, and
|
|
||||||
also other build systems such as Bazel and Blaze, making it possible to
|
|
||||||
construct analysis tools that work in all these environments.
|
|
||||||
Tools such as errcheck and staticcheck were essentially unavailable to
|
|
||||||
the Go community at Google, and some of Google's internal tools for Go
|
|
||||||
are unavailable externally.
|
|
||||||
This new package provides a uniform way to obtain package metadata by
|
|
||||||
querying each of these build systems, optionally supporting their
|
|
||||||
preferred command-line notations for packages, so that tools integrate
|
|
||||||
neatly with users' build environments. The Metadata query function
|
|
||||||
executes an external query tool appropriate to the current workspace.
|
|
||||||
|
|
||||||
Loading packages always returns the complete import graph "all the way down",
|
|
||||||
even if all you want is information about a single package, because the query
|
|
||||||
mechanisms of all the build systems we currently support ({go,vgo} list, and
|
|
||||||
blaze/bazel aspect-based query) cannot provide detailed information
|
|
||||||
about one package without visiting all its dependencies too, so there is
|
|
||||||
no additional asymptotic cost to providing transitive information.
|
|
||||||
(This property might not be true of a hypothetical 5th build system.)
|
|
||||||
|
|
||||||
In calls to TypeCheck, all initial packages, and any package that
|
|
||||||
transitively depends on one of them, must be loaded from source.
|
|
||||||
Consider A->B->C->D->E: if A,C are initial, A,B,C must be loaded from
|
|
||||||
source; D may be loaded from export data, and E may not be loaded at all
|
|
||||||
(though it's possible that D's export data mentions it, so a
|
|
||||||
types.Package may be created for it and exposed.)
|
|
||||||
|
|
||||||
The old loader had a feature to suppress type-checking of function
|
|
||||||
bodies on a per-package basis, primarily intended to reduce the work of
|
|
||||||
obtaining type information for imported packages. Now that imports are
|
|
||||||
satisfied by export data, the optimization no longer seems necessary.
|
|
||||||
|
|
||||||
Despite some early attempts, the old loader did not exploit export data,
|
|
||||||
instead always using the equivalent of WholeProgram mode. This was due
|
|
||||||
to the complexity of mixing source and export data packages (now
|
|
||||||
resolved by the upward traversal mentioned above), and because export data
|
|
||||||
files were nearly always missing or stale. Now that 'go build' supports
|
|
||||||
caching, all the underlying build systems can guarantee to produce
|
|
||||||
export data in a reasonable (amortized) time.
|
|
||||||
|
|
||||||
Test "main" packages synthesized by the build system are now reported as
|
|
||||||
first-class packages, avoiding the need for clients (such as go/ssa) to
|
|
||||||
reinvent this generation logic.
|
|
||||||
|
|
||||||
One way in which go/packages is simpler than the old loader is in its
|
|
||||||
treatment of in-package tests. In-package tests are packages that
|
|
||||||
consist of all the files of the library under test, plus the test files.
|
|
||||||
The old loader constructed in-package tests by a two-phase process of
|
|
||||||
mutation called "augmentation": first it would construct and type check
|
|
||||||
all the ordinary library packages and type-check the packages that
|
|
||||||
depend on them; then it would add more (test) files to the package and
|
|
||||||
type-check again. This two-phase approach had four major problems:
|
|
||||||
1) in processing the tests, the loader modified the library package,
|
|
||||||
leaving no way for a client application to see both the test
|
|
||||||
package and the library package; one would mutate into the other.
|
|
||||||
2) because test files can declare additional methods on types defined in
|
|
||||||
the library portion of the package, the dispatch of method calls in
|
|
||||||
the library portion was affected by the presence of the test files.
|
|
||||||
This should have been a clue that the packages were logically
|
|
||||||
different.
|
|
||||||
3) this model of "augmentation" assumed at most one in-package test
|
|
||||||
per library package, which is true of projects using 'go build',
|
|
||||||
but not other build systems.
|
|
||||||
4) because of the two-phase nature of test processing, all packages that
|
|
||||||
import the library package had to be processed before augmentation,
|
|
||||||
forcing a "one-shot" API and preventing the client from calling Load
|
|
||||||
in several times in sequence as is now possible in WholeProgram mode.
|
|
||||||
(TypeCheck mode has a similar one-shot restriction for a different reason.)
|
|
||||||
|
|
||||||
Early drafts of this package supported "multi-shot" operation.
|
|
||||||
Although it allowed clients to make a sequence of calls (or concurrent
|
|
||||||
calls) to Load, building up the graph of Packages incrementally,
|
|
||||||
it was of marginal value: it complicated the API
|
|
||||||
(since it allowed some options to vary across calls but not others),
|
|
||||||
it complicated the implementation,
|
|
||||||
it cannot be made to work in Types mode, as explained above,
|
|
||||||
and it was less efficient than making one combined call (when this is possible).
|
|
||||||
Among the clients we have inspected, none made multiple calls to load
|
|
||||||
but could not be easily and satisfactorily modified to make only a single call.
|
|
||||||
However, applications changes may be required.
|
|
||||||
For example, the ssadump command loads the user-specified packages
|
|
||||||
and in addition the runtime package. It is tempting to simply append
|
|
||||||
"runtime" to the user-provided list, but that does not work if the user
|
|
||||||
specified an ad-hoc package such as [a.go b.go].
|
|
||||||
Instead, ssadump no longer requests the runtime package,
|
|
||||||
but seeks it among the dependencies of the user-specified packages,
|
|
||||||
and emits an error if it is not found.
|
|
||||||
|
|
||||||
Overlays: The Overlay field in the Config allows providing alternate contents
|
|
||||||
for Go source files, by providing a mapping from file path to contents.
|
|
||||||
go/packages will pull in new imports added in overlay files when go/packages
|
|
||||||
is run in LoadImports mode or greater.
|
|
||||||
Overlay support for the go list driver isn't complete yet: if the file doesn't
|
|
||||||
exist on disk, it will only be recognized in an overlay if it is a non-test file
|
|
||||||
and the package would be reported even without the overlay.
|
|
||||||
|
|
||||||
Questions & Tasks
|
|
||||||
|
|
||||||
- Add GOARCH/GOOS?
|
|
||||||
They are not portable concepts, but could be made portable.
|
|
||||||
Our goal has been to allow users to express themselves using the conventions
|
|
||||||
of the underlying build system: if the build system honors GOARCH
|
|
||||||
during a build and during a metadata query, then so should
|
|
||||||
applications built atop that query mechanism.
|
|
||||||
Conversely, if the target architecture of the build is determined by
|
|
||||||
command-line flags, the application can pass the relevant
|
|
||||||
flags through to the build system using a command such as:
|
|
||||||
myapp -query_flag="--cpu=amd64" -query_flag="--os=darwin"
|
|
||||||
However, this approach is low-level, unwieldy, and non-portable.
|
|
||||||
GOOS and GOARCH seem important enough to warrant a dedicated option.
|
|
||||||
|
|
||||||
- How should we handle partial failures such as a mixture of good and
|
|
||||||
malformed patterns, existing and non-existent packages, successful and
|
|
||||||
failed builds, import failures, import cycles, and so on, in a call to
|
|
||||||
Load?
|
|
||||||
|
|
||||||
- Support bazel, blaze, and go1.10 list, not just go1.11 list.
|
|
||||||
|
|
||||||
- Handle (and test) various partial success cases, e.g.
|
|
||||||
a mixture of good packages and:
|
|
||||||
invalid patterns
|
|
||||||
nonexistent packages
|
|
||||||
empty packages
|
|
||||||
packages with malformed package or import declarations
|
|
||||||
unreadable files
|
|
||||||
import cycles
|
|
||||||
other parse errors
|
|
||||||
type errors
|
|
||||||
Make sure we record errors at the correct place in the graph.
|
|
||||||
|
|
||||||
- Missing packages among initial arguments are not reported.
|
|
||||||
Return bogus packages for them, like golist does.
|
|
||||||
|
|
||||||
- "undeclared name" errors (for example) are reported out of source file
|
|
||||||
order. I suspect this is due to the breadth-first resolution now used
|
|
||||||
by go/types. Is that a bug? Discuss with gri.
|
|
||||||
|
|
||||||
*/
|
|
||||||
-101
@@ -1,101 +0,0 @@
|
|||||||
// Copyright 2018 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.
|
|
||||||
|
|
||||||
// This file enables an external tool to intercept package requests.
|
|
||||||
// If the tool is present then its results are used in preference to
|
|
||||||
// the go list command.
|
|
||||||
|
|
||||||
package packages
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The Driver Protocol
|
|
||||||
//
|
|
||||||
// The driver, given the inputs to a call to Load, returns metadata about the packages specified.
|
|
||||||
// This allows for different build systems to support go/packages by telling go/packages how the
|
|
||||||
// packages' source is organized.
|
|
||||||
// The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in
|
|
||||||
// the path as gopackagesdriver. It's given the inputs to load in its argv. See the package
|
|
||||||
// documentation in doc.go for the full description of the patterns that need to be supported.
|
|
||||||
// A driver receives as a JSON-serialized driverRequest struct in standard input and will
|
|
||||||
// produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output.
|
|
||||||
|
|
||||||
// driverRequest is used to provide the portion of Load's Config that is needed by a driver.
|
|
||||||
type driverRequest struct {
|
|
||||||
Mode LoadMode `json:"mode"`
|
|
||||||
// Env specifies the environment the underlying build system should be run in.
|
|
||||||
Env []string `json:"env"`
|
|
||||||
// BuildFlags are flags that should be passed to the underlying build system.
|
|
||||||
BuildFlags []string `json:"build_flags"`
|
|
||||||
// Tests specifies whether the patterns should also return test packages.
|
|
||||||
Tests bool `json:"tests"`
|
|
||||||
// Overlay maps file paths (relative to the driver's working directory) to the byte contents
|
|
||||||
// of overlay files.
|
|
||||||
Overlay map[string][]byte `json:"overlay"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// findExternalDriver returns the file path of a tool that supplies
|
|
||||||
// the build system package structure, or "" if not found."
|
|
||||||
// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
|
|
||||||
// value, otherwise it searches for a binary named gopackagesdriver on the PATH.
|
|
||||||
func findExternalDriver(cfg *Config) driver {
|
|
||||||
const toolPrefix = "GOPACKAGESDRIVER="
|
|
||||||
tool := ""
|
|
||||||
for _, env := range cfg.Env {
|
|
||||||
if val := strings.TrimPrefix(env, toolPrefix); val != env {
|
|
||||||
tool = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tool != "" && tool == "off" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if tool == "" {
|
|
||||||
var err error
|
|
||||||
tool, err = exec.LookPath("gopackagesdriver")
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return func(cfg *Config, words ...string) (*driverResponse, error) {
|
|
||||||
req, err := json.Marshal(driverRequest{
|
|
||||||
Mode: cfg.Mode,
|
|
||||||
Env: cfg.Env,
|
|
||||||
BuildFlags: cfg.BuildFlags,
|
|
||||||
Tests: cfg.Tests,
|
|
||||||
Overlay: cfg.Overlay,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to encode message to driver tool: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
stderr := new(bytes.Buffer)
|
|
||||||
cmd := exec.CommandContext(cfg.Context, tool, words...)
|
|
||||||
cmd.Dir = cfg.Dir
|
|
||||||
cmd.Env = cfg.Env
|
|
||||||
cmd.Stdin = bytes.NewReader(req)
|
|
||||||
cmd.Stdout = buf
|
|
||||||
cmd.Stderr = stderr
|
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
|
|
||||||
}
|
|
||||||
if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" {
|
|
||||||
fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
var response driverResponse
|
|
||||||
if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &response, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1107
File diff suppressed because it is too large
Load Diff
-83
@@ -1,83 +0,0 @@
|
|||||||
// Copyright 2018 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.
|
|
||||||
|
|
||||||
package packages
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/gocommand"
|
|
||||||
)
|
|
||||||
|
|
||||||
// determineRootDirs returns a mapping from absolute directories that could
|
|
||||||
// contain code to their corresponding import path prefixes.
|
|
||||||
func (state *golistState) determineRootDirs() (map[string]string, error) {
|
|
||||||
env, err := state.getEnv()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if env["GOMOD"] != "" {
|
|
||||||
state.rootsOnce.Do(func() {
|
|
||||||
state.rootDirs, state.rootDirsError = state.determineRootDirsModules()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
state.rootsOnce.Do(func() {
|
|
||||||
state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return state.rootDirs, state.rootDirsError
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state *golistState) determineRootDirsModules() (map[string]string, error) {
|
|
||||||
// List all of the modules--the first will be the directory for the main
|
|
||||||
// module. Any replaced modules will also need to be treated as roots.
|
|
||||||
// Editing files in the module cache isn't a great idea, so we don't
|
|
||||||
// plan to ever support that.
|
|
||||||
out, err := state.invokeGo("list", "-m", "-json", "all")
|
|
||||||
if err != nil {
|
|
||||||
// 'go list all' will fail if we're outside of a module and
|
|
||||||
// GO111MODULE=on. Try falling back without 'all'.
|
|
||||||
var innerErr error
|
|
||||||
out, innerErr = state.invokeGo("list", "-m", "-json")
|
|
||||||
if innerErr != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
roots := map[string]string{}
|
|
||||||
modules := map[string]string{}
|
|
||||||
var i int
|
|
||||||
for dec := json.NewDecoder(out); dec.More(); {
|
|
||||||
mod := new(gocommand.ModuleJSON)
|
|
||||||
if err := dec.Decode(mod); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if mod.Dir != "" && mod.Path != "" {
|
|
||||||
// This is a valid module; add it to the map.
|
|
||||||
absDir, err := filepath.Abs(mod.Dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
modules[absDir] = mod.Path
|
|
||||||
// The first result is the main module.
|
|
||||||
if i == 0 || mod.Replace != nil && mod.Replace.Path != "" {
|
|
||||||
roots[absDir] = mod.Path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return roots, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
|
|
||||||
m := map[string]string{}
|
|
||||||
for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) {
|
|
||||||
absDir, err := filepath.Abs(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m[filepath.Join(absDir, "src")] = ""
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
-57
@@ -1,57 +0,0 @@
|
|||||||
// Copyright 2019 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.
|
|
||||||
|
|
||||||
package packages
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var allModes = []LoadMode{
|
|
||||||
NeedName,
|
|
||||||
NeedFiles,
|
|
||||||
NeedCompiledGoFiles,
|
|
||||||
NeedImports,
|
|
||||||
NeedDeps,
|
|
||||||
NeedExportFile,
|
|
||||||
NeedTypes,
|
|
||||||
NeedSyntax,
|
|
||||||
NeedTypesInfo,
|
|
||||||
NeedTypesSizes,
|
|
||||||
}
|
|
||||||
|
|
||||||
var modeStrings = []string{
|
|
||||||
"NeedName",
|
|
||||||
"NeedFiles",
|
|
||||||
"NeedCompiledGoFiles",
|
|
||||||
"NeedImports",
|
|
||||||
"NeedDeps",
|
|
||||||
"NeedExportFile",
|
|
||||||
"NeedTypes",
|
|
||||||
"NeedSyntax",
|
|
||||||
"NeedTypesInfo",
|
|
||||||
"NeedTypesSizes",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mod LoadMode) String() string {
|
|
||||||
m := mod
|
|
||||||
if m == 0 {
|
|
||||||
return "LoadMode(0)"
|
|
||||||
}
|
|
||||||
var out []string
|
|
||||||
for i, x := range allModes {
|
|
||||||
if x > m {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (m & x) != 0 {
|
|
||||||
out = append(out, modeStrings[i])
|
|
||||||
m = m ^ x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if m != 0 {
|
|
||||||
out = append(out, "Unknown")
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("LoadMode(%s)", strings.Join(out, "|"))
|
|
||||||
}
|
|
||||||
-1348
File diff suppressed because it is too large
Load Diff
-59
@@ -1,59 +0,0 @@
|
|||||||
// Copyright 2018 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.
|
|
||||||
|
|
||||||
package packages
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Visit visits all the packages in the import graph whose roots are
|
|
||||||
// pkgs, calling the optional pre function the first time each package
|
|
||||||
// is encountered (preorder), and the optional post function after a
|
|
||||||
// package's dependencies have been visited (postorder).
|
|
||||||
// The boolean result of pre(pkg) determines whether
|
|
||||||
// the imports of package pkg are visited.
|
|
||||||
func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
|
|
||||||
seen := make(map[*Package]bool)
|
|
||||||
var visit func(*Package)
|
|
||||||
visit = func(pkg *Package) {
|
|
||||||
if !seen[pkg] {
|
|
||||||
seen[pkg] = true
|
|
||||||
|
|
||||||
if pre == nil || pre(pkg) {
|
|
||||||
paths := make([]string, 0, len(pkg.Imports))
|
|
||||||
for path := range pkg.Imports {
|
|
||||||
paths = append(paths, path)
|
|
||||||
}
|
|
||||||
sort.Strings(paths) // Imports is a map, this makes visit stable
|
|
||||||
for _, path := range paths {
|
|
||||||
visit(pkg.Imports[path])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if post != nil {
|
|
||||||
post(pkg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
visit(pkg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintErrors prints to os.Stderr the accumulated errors of all
|
|
||||||
// packages in the import graph rooted at pkgs, dependencies first.
|
|
||||||
// PrintErrors returns the number of errors printed.
|
|
||||||
func PrintErrors(pkgs []*Package) int {
|
|
||||||
var n int
|
|
||||||
Visit(pkgs, nil, func(pkg *Package) {
|
|
||||||
for _, err := range pkg.Errors {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
-752
@@ -1,752 +0,0 @@
|
|||||||
// Copyright 2018 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.
|
|
||||||
|
|
||||||
// Package objectpath defines a naming scheme for types.Objects
|
|
||||||
// (that is, named entities in Go programs) relative to their enclosing
|
|
||||||
// package.
|
|
||||||
//
|
|
||||||
// Type-checker objects are canonical, so they are usually identified by
|
|
||||||
// their address in memory (a pointer), but a pointer has meaning only
|
|
||||||
// within one address space. By contrast, objectpath names allow the
|
|
||||||
// identity of an object to be sent from one program to another,
|
|
||||||
// establishing a correspondence between types.Object variables that are
|
|
||||||
// distinct but logically equivalent.
|
|
||||||
//
|
|
||||||
// A single object may have multiple paths. In this example,
|
|
||||||
//
|
|
||||||
// type A struct{ X int }
|
|
||||||
// type B A
|
|
||||||
//
|
|
||||||
// the field X has two paths due to its membership of both A and B.
|
|
||||||
// The For(obj) function always returns one of these paths, arbitrarily
|
|
||||||
// but consistently.
|
|
||||||
package objectpath
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"go/types"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/typeparams"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Path is an opaque name that identifies a types.Object
|
|
||||||
// relative to its package. Conceptually, the name consists of a
|
|
||||||
// sequence of destructuring operations applied to the package scope
|
|
||||||
// to obtain the original object.
|
|
||||||
// The name does not include the package itself.
|
|
||||||
type Path string
|
|
||||||
|
|
||||||
// Encoding
|
|
||||||
//
|
|
||||||
// An object path is a textual and (with training) human-readable encoding
|
|
||||||
// of a sequence of destructuring operators, starting from a types.Package.
|
|
||||||
// The sequences represent a path through the package/object/type graph.
|
|
||||||
// We classify these operators by their type:
|
|
||||||
//
|
|
||||||
// PO package->object Package.Scope.Lookup
|
|
||||||
// OT object->type Object.Type
|
|
||||||
// TT type->type Type.{Elem,Key,Params,Results,Underlying} [EKPRU]
|
|
||||||
// TO type->object Type.{At,Field,Method,Obj} [AFMO]
|
|
||||||
//
|
|
||||||
// All valid paths start with a package and end at an object
|
|
||||||
// and thus may be defined by the regular language:
|
|
||||||
//
|
|
||||||
// objectpath = PO (OT TT* TO)*
|
|
||||||
//
|
|
||||||
// The concrete encoding follows directly:
|
|
||||||
// - The only PO operator is Package.Scope.Lookup, which requires an identifier.
|
|
||||||
// - The only OT operator is Object.Type,
|
|
||||||
// which we encode as '.' because dot cannot appear in an identifier.
|
|
||||||
// - The TT operators are encoded as [EKPRUTC];
|
|
||||||
// one of these (TypeParam) requires an integer operand,
|
|
||||||
// which is encoded as a string of decimal digits.
|
|
||||||
// - The TO operators are encoded as [AFMO];
|
|
||||||
// three of these (At,Field,Method) require an integer operand,
|
|
||||||
// which is encoded as a string of decimal digits.
|
|
||||||
// These indices are stable across different representations
|
|
||||||
// of the same package, even source and export data.
|
|
||||||
// The indices used are implementation specific and may not correspond to
|
|
||||||
// the argument to the go/types function.
|
|
||||||
//
|
|
||||||
// In the example below,
|
|
||||||
//
|
|
||||||
// package p
|
|
||||||
//
|
|
||||||
// type T interface {
|
|
||||||
// f() (a string, b struct{ X int })
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// field X has the path "T.UM0.RA1.F0",
|
|
||||||
// representing the following sequence of operations:
|
|
||||||
//
|
|
||||||
// p.Lookup("T") T
|
|
||||||
// .Type().Underlying().Method(0). f
|
|
||||||
// .Type().Results().At(1) b
|
|
||||||
// .Type().Field(0) X
|
|
||||||
//
|
|
||||||
// The encoding is not maximally compact---every R or P is
|
|
||||||
// followed by an A, for example---but this simplifies the
|
|
||||||
// encoder and decoder.
|
|
||||||
const (
|
|
||||||
// object->type operators
|
|
||||||
opType = '.' // .Type() (Object)
|
|
||||||
|
|
||||||
// type->type operators
|
|
||||||
opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map)
|
|
||||||
opKey = 'K' // .Key() (Map)
|
|
||||||
opParams = 'P' // .Params() (Signature)
|
|
||||||
opResults = 'R' // .Results() (Signature)
|
|
||||||
opUnderlying = 'U' // .Underlying() (Named)
|
|
||||||
opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature)
|
|
||||||
opConstraint = 'C' // .Constraint() (TypeParam)
|
|
||||||
|
|
||||||
// type->object operators
|
|
||||||
opAt = 'A' // .At(i) (Tuple)
|
|
||||||
opField = 'F' // .Field(i) (Struct)
|
|
||||||
opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored)
|
|
||||||
opObj = 'O' // .Obj() (Named, TypeParam)
|
|
||||||
)
|
|
||||||
|
|
||||||
// For is equivalent to new(Encoder).For(obj).
|
|
||||||
//
|
|
||||||
// It may be more efficient to reuse a single Encoder across several calls.
|
|
||||||
func For(obj types.Object) (Path, error) {
|
|
||||||
return new(Encoder).For(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// An Encoder amortizes the cost of encoding the paths of multiple objects.
|
|
||||||
// The zero value of an Encoder is ready to use.
|
|
||||||
type Encoder struct {
|
|
||||||
scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects
|
|
||||||
}
|
|
||||||
|
|
||||||
// For returns the path to an object relative to its package,
|
|
||||||
// or an error if the object is not accessible from the package's Scope.
|
|
||||||
//
|
|
||||||
// The For function guarantees to return a path only for the following objects:
|
|
||||||
// - package-level types
|
|
||||||
// - exported package-level non-types
|
|
||||||
// - methods
|
|
||||||
// - parameter and result variables
|
|
||||||
// - struct fields
|
|
||||||
// These objects are sufficient to define the API of their package.
|
|
||||||
// The objects described by a package's export data are drawn from this set.
|
|
||||||
//
|
|
||||||
// The set of objects accessible from a package's Scope depends on
|
|
||||||
// whether the package was produced by type-checking syntax, or
|
|
||||||
// reading export data; the latter may have a smaller Scope since
|
|
||||||
// export data trims objects that are not reachable from an exported
|
|
||||||
// declaration. For example, the For function will return a path for
|
|
||||||
// an exported method of an unexported type that is not reachable
|
|
||||||
// from any public declaration; this path will cause the Object
|
|
||||||
// function to fail if called on a package loaded from export data.
|
|
||||||
// TODO(adonovan): is this a bug or feature? Should this package
|
|
||||||
// compute accessibility in the same way?
|
|
||||||
//
|
|
||||||
// For does not return a path for predeclared names, imported package
|
|
||||||
// names, local names, and unexported package-level names (except
|
|
||||||
// types).
|
|
||||||
//
|
|
||||||
// Example: given this definition,
|
|
||||||
//
|
|
||||||
// package p
|
|
||||||
//
|
|
||||||
// type T interface {
|
|
||||||
// f() (a string, b struct{ X int })
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// For(X) would return a path that denotes the following sequence of operations:
|
|
||||||
//
|
|
||||||
// p.Scope().Lookup("T") (TypeName T)
|
|
||||||
// .Type().Underlying().Method(0). (method Func f)
|
|
||||||
// .Type().Results().At(1) (field Var b)
|
|
||||||
// .Type().Field(0) (field Var X)
|
|
||||||
//
|
|
||||||
// where p is the package (*types.Package) to which X belongs.
|
|
||||||
func (enc *Encoder) For(obj types.Object) (Path, error) {
|
|
||||||
pkg := obj.Pkg()
|
|
||||||
|
|
||||||
// This table lists the cases of interest.
|
|
||||||
//
|
|
||||||
// Object Action
|
|
||||||
// ------ ------
|
|
||||||
// nil reject
|
|
||||||
// builtin reject
|
|
||||||
// pkgname reject
|
|
||||||
// label reject
|
|
||||||
// var
|
|
||||||
// package-level accept
|
|
||||||
// func param/result accept
|
|
||||||
// local reject
|
|
||||||
// struct field accept
|
|
||||||
// const
|
|
||||||
// package-level accept
|
|
||||||
// local reject
|
|
||||||
// func
|
|
||||||
// package-level accept
|
|
||||||
// init functions reject
|
|
||||||
// concrete method accept
|
|
||||||
// interface method accept
|
|
||||||
// type
|
|
||||||
// package-level accept
|
|
||||||
// local reject
|
|
||||||
//
|
|
||||||
// The only accessible package-level objects are members of pkg itself.
|
|
||||||
//
|
|
||||||
// The cases are handled in four steps:
|
|
||||||
//
|
|
||||||
// 1. reject nil and builtin
|
|
||||||
// 2. accept package-level objects
|
|
||||||
// 3. reject obviously invalid objects
|
|
||||||
// 4. search the API for the path to the param/result/field/method.
|
|
||||||
|
|
||||||
// 1. reference to nil or builtin?
|
|
||||||
if pkg == nil {
|
|
||||||
return "", fmt.Errorf("predeclared %s has no path", obj)
|
|
||||||
}
|
|
||||||
scope := pkg.Scope()
|
|
||||||
|
|
||||||
// 2. package-level object?
|
|
||||||
if scope.Lookup(obj.Name()) == obj {
|
|
||||||
// Only exported objects (and non-exported types) have a path.
|
|
||||||
// Non-exported types may be referenced by other objects.
|
|
||||||
if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() {
|
|
||||||
return "", fmt.Errorf("no path for non-exported %v", obj)
|
|
||||||
}
|
|
||||||
return Path(obj.Name()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Not a package-level object.
|
|
||||||
// Reject obviously non-viable cases.
|
|
||||||
switch obj := obj.(type) {
|
|
||||||
case *types.TypeName:
|
|
||||||
if _, ok := obj.Type().(*typeparams.TypeParam); !ok {
|
|
||||||
// With the exception of type parameters, only package-level type names
|
|
||||||
// have a path.
|
|
||||||
return "", fmt.Errorf("no path for %v", obj)
|
|
||||||
}
|
|
||||||
case *types.Const, // Only package-level constants have a path.
|
|
||||||
*types.Label, // Labels are function-local.
|
|
||||||
*types.PkgName: // PkgNames are file-local.
|
|
||||||
return "", fmt.Errorf("no path for %v", obj)
|
|
||||||
|
|
||||||
case *types.Var:
|
|
||||||
// Could be:
|
|
||||||
// - a field (obj.IsField())
|
|
||||||
// - a func parameter or result
|
|
||||||
// - a local var.
|
|
||||||
// Sadly there is no way to distinguish
|
|
||||||
// a param/result from a local
|
|
||||||
// so we must proceed to the find.
|
|
||||||
|
|
||||||
case *types.Func:
|
|
||||||
// A func, if not package-level, must be a method.
|
|
||||||
if recv := obj.Type().(*types.Signature).Recv(); recv == nil {
|
|
||||||
return "", fmt.Errorf("func is not a method: %v", obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
if path, ok := enc.concreteMethod(obj); ok {
|
|
||||||
// Fast path for concrete methods that avoids looping over scope.
|
|
||||||
return path, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Search the API for the path to the var (field/param/result) or method.
|
|
||||||
|
|
||||||
// First inspect package-level named types.
|
|
||||||
// In the presence of path aliases, these give
|
|
||||||
// the best paths because non-types may
|
|
||||||
// refer to types, but not the reverse.
|
|
||||||
empty := make([]byte, 0, 48) // initial space
|
|
||||||
objs := enc.scopeObjects(scope)
|
|
||||||
for _, o := range objs {
|
|
||||||
tname, ok := o.(*types.TypeName)
|
|
||||||
if !ok {
|
|
||||||
continue // handle non-types in second pass
|
|
||||||
}
|
|
||||||
|
|
||||||
path := append(empty, o.Name()...)
|
|
||||||
path = append(path, opType)
|
|
||||||
|
|
||||||
T := o.Type()
|
|
||||||
|
|
||||||
if tname.IsAlias() {
|
|
||||||
// type alias
|
|
||||||
if r := find(obj, T, path, nil); r != nil {
|
|
||||||
return Path(r), nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if named, _ := T.(*types.Named); named != nil {
|
|
||||||
if r := findTypeParam(obj, typeparams.ForNamed(named), path, nil); r != nil {
|
|
||||||
// generic named type
|
|
||||||
return Path(r), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// defined (named) type
|
|
||||||
if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil {
|
|
||||||
return Path(r), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then inspect everything else:
|
|
||||||
// non-types, and declared methods of defined types.
|
|
||||||
for _, o := range objs {
|
|
||||||
path := append(empty, o.Name()...)
|
|
||||||
if _, ok := o.(*types.TypeName); !ok {
|
|
||||||
if o.Exported() {
|
|
||||||
// exported non-type (const, var, func)
|
|
||||||
if r := find(obj, o.Type(), append(path, opType), nil); r != nil {
|
|
||||||
return Path(r), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inspect declared methods of defined types.
|
|
||||||
if T, ok := o.Type().(*types.Named); ok {
|
|
||||||
path = append(path, opType)
|
|
||||||
// The method index here is always with respect
|
|
||||||
// to the underlying go/types data structures,
|
|
||||||
// which ultimately derives from source order
|
|
||||||
// and must be preserved by export data.
|
|
||||||
for i := 0; i < T.NumMethods(); i++ {
|
|
||||||
m := T.Method(i)
|
|
||||||
path2 := appendOpArg(path, opMethod, i)
|
|
||||||
if m == obj {
|
|
||||||
return Path(path2), nil // found declared method
|
|
||||||
}
|
|
||||||
if r := find(obj, m.Type(), append(path2, opType), nil); r != nil {
|
|
||||||
return Path(r), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path())
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendOpArg(path []byte, op byte, arg int) []byte {
|
|
||||||
path = append(path, op)
|
|
||||||
path = strconv.AppendInt(path, int64(arg), 10)
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
// concreteMethod returns the path for meth, which must have a non-nil receiver.
|
|
||||||
// The second return value indicates success and may be false if the method is
|
|
||||||
// an interface method or if it is an instantiated method.
|
|
||||||
//
|
|
||||||
// This function is just an optimization that avoids the general scope walking
|
|
||||||
// approach. You are expected to fall back to the general approach if this
|
|
||||||
// function fails.
|
|
||||||
func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
|
|
||||||
// Concrete methods can only be declared on package-scoped named types. For
|
|
||||||
// that reason we can skip the expensive walk over the package scope: the
|
|
||||||
// path will always be package -> named type -> method. We can trivially get
|
|
||||||
// the type name from the receiver, and only have to look over the type's
|
|
||||||
// methods to find the method index.
|
|
||||||
//
|
|
||||||
// Methods on generic types require special consideration, however. Consider
|
|
||||||
// the following package:
|
|
||||||
//
|
|
||||||
// L1: type S[T any] struct{}
|
|
||||||
// L2: func (recv S[A]) Foo() { recv.Bar() }
|
|
||||||
// L3: func (recv S[B]) Bar() { }
|
|
||||||
// L4: type Alias = S[int]
|
|
||||||
// L5: func _[T any]() { var s S[int]; s.Foo() }
|
|
||||||
//
|
|
||||||
// The receivers of methods on generic types are instantiations. L2 and L3
|
|
||||||
// instantiate S with the type-parameters A and B, which are scoped to the
|
|
||||||
// respective methods. L4 and L5 each instantiate S with int. Each of these
|
|
||||||
// instantiations has its own method set, full of methods (and thus objects)
|
|
||||||
// with receivers whose types are the respective instantiations. In other
|
|
||||||
// words, we have
|
|
||||||
//
|
|
||||||
// S[A].Foo, S[A].Bar
|
|
||||||
// S[B].Foo, S[B].Bar
|
|
||||||
// S[int].Foo, S[int].Bar
|
|
||||||
//
|
|
||||||
// We may thus be trying to produce object paths for any of these objects.
|
|
||||||
//
|
|
||||||
// S[A].Foo and S[B].Bar are the origin methods, and their paths are S.Foo
|
|
||||||
// and S.Bar, which are the paths that this function naturally produces.
|
|
||||||
//
|
|
||||||
// S[A].Bar, S[B].Foo, and both methods on S[int] are instantiations that
|
|
||||||
// don't correspond to the origin methods. For S[int], this is significant.
|
|
||||||
// The most precise object path for S[int].Foo, for example, is Alias.Foo,
|
|
||||||
// not S.Foo. Our function, however, would produce S.Foo, which would
|
|
||||||
// resolve to a different object.
|
|
||||||
//
|
|
||||||
// For S[A].Bar and S[B].Foo it could be argued that S.Bar and S.Foo are
|
|
||||||
// still the correct paths, since only the origin methods have meaningful
|
|
||||||
// paths. But this is likely only true for trivial cases and has edge cases.
|
|
||||||
// Since this function is only an optimization, we err on the side of giving
|
|
||||||
// up, deferring to the slower but definitely correct algorithm. Most users
|
|
||||||
// of objectpath will only be giving us origin methods, anyway, as referring
|
|
||||||
// to instantiated methods is usually not useful.
|
|
||||||
|
|
||||||
if typeparams.OriginMethod(meth) != meth {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
recvT := meth.Type().(*types.Signature).Recv().Type()
|
|
||||||
if ptr, ok := recvT.(*types.Pointer); ok {
|
|
||||||
recvT = ptr.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
named, ok := recvT.(*types.Named)
|
|
||||||
if !ok {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
if types.IsInterface(named) {
|
|
||||||
// Named interfaces don't have to be package-scoped
|
|
||||||
//
|
|
||||||
// TODO(dominikh): opt: if scope.Lookup(name) == named, then we can apply this optimization to interface
|
|
||||||
// methods, too, I think.
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preallocate space for the name, opType, opMethod, and some digits.
|
|
||||||
name := named.Obj().Name()
|
|
||||||
path := make([]byte, 0, len(name)+8)
|
|
||||||
path = append(path, name...)
|
|
||||||
path = append(path, opType)
|
|
||||||
|
|
||||||
// Method indices are w.r.t. the go/types data structures,
|
|
||||||
// ultimately deriving from source order,
|
|
||||||
// which is preserved by export data.
|
|
||||||
for i := 0; i < named.NumMethods(); i++ {
|
|
||||||
if named.Method(i) == meth {
|
|
||||||
path = appendOpArg(path, opMethod, i)
|
|
||||||
return Path(path), true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Due to golang/go#59944, go/types fails to associate the receiver with
|
|
||||||
// certain methods on cgo types.
|
|
||||||
//
|
|
||||||
// TODO(rfindley): replace this panic once golang/go#59944 is fixed in all Go
|
|
||||||
// versions gopls supports.
|
|
||||||
return "", false
|
|
||||||
// panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// find finds obj within type T, returning the path to it, or nil if not found.
|
|
||||||
//
|
|
||||||
// The seen map is used to short circuit cycles through type parameters. If
|
|
||||||
// nil, it will be allocated as necessary.
|
|
||||||
func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte {
|
|
||||||
switch T := T.(type) {
|
|
||||||
case *types.Basic, *types.Named:
|
|
||||||
// Named types belonging to pkg were handled already,
|
|
||||||
// so T must belong to another package. No path.
|
|
||||||
return nil
|
|
||||||
case *types.Pointer:
|
|
||||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
|
||||||
case *types.Slice:
|
|
||||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
|
||||||
case *types.Array:
|
|
||||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
|
||||||
case *types.Chan:
|
|
||||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
|
||||||
case *types.Map:
|
|
||||||
if r := find(obj, T.Key(), append(path, opKey), seen); r != nil {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
|
||||||
case *types.Signature:
|
|
||||||
if r := findTypeParam(obj, typeparams.ForSignature(T), path, seen); r != nil {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
if r := find(obj, T.Params(), append(path, opParams), seen); r != nil {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
return find(obj, T.Results(), append(path, opResults), seen)
|
|
||||||
case *types.Struct:
|
|
||||||
for i := 0; i < T.NumFields(); i++ {
|
|
||||||
fld := T.Field(i)
|
|
||||||
path2 := appendOpArg(path, opField, i)
|
|
||||||
if fld == obj {
|
|
||||||
return path2 // found field var
|
|
||||||
}
|
|
||||||
if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
case *types.Tuple:
|
|
||||||
for i := 0; i < T.Len(); i++ {
|
|
||||||
v := T.At(i)
|
|
||||||
path2 := appendOpArg(path, opAt, i)
|
|
||||||
if v == obj {
|
|
||||||
return path2 // found param/result var
|
|
||||||
}
|
|
||||||
if r := find(obj, v.Type(), append(path2, opType), seen); r != nil {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
case *types.Interface:
|
|
||||||
for i := 0; i < T.NumMethods(); i++ {
|
|
||||||
m := T.Method(i)
|
|
||||||
path2 := appendOpArg(path, opMethod, i)
|
|
||||||
if m == obj {
|
|
||||||
return path2 // found interface method
|
|
||||||
}
|
|
||||||
if r := find(obj, m.Type(), append(path2, opType), seen); r != nil {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
case *typeparams.TypeParam:
|
|
||||||
name := T.Obj()
|
|
||||||
if name == obj {
|
|
||||||
return append(path, opObj)
|
|
||||||
}
|
|
||||||
if seen[name] {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if seen == nil {
|
|
||||||
seen = make(map[*types.TypeName]bool)
|
|
||||||
}
|
|
||||||
seen[name] = true
|
|
||||||
if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
panic(T)
|
|
||||||
}
|
|
||||||
|
|
||||||
func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte {
|
|
||||||
for i := 0; i < list.Len(); i++ {
|
|
||||||
tparam := list.At(i)
|
|
||||||
path2 := appendOpArg(path, opTypeParam, i)
|
|
||||||
if r := find(obj, tparam, path2, seen); r != nil {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Object returns the object denoted by path p within the package pkg.
|
|
||||||
func Object(pkg *types.Package, p Path) (types.Object, error) {
|
|
||||||
pathstr := string(p)
|
|
||||||
if pathstr == "" {
|
|
||||||
return nil, fmt.Errorf("empty path")
|
|
||||||
}
|
|
||||||
|
|
||||||
var pkgobj, suffix string
|
|
||||||
if dot := strings.IndexByte(pathstr, opType); dot < 0 {
|
|
||||||
pkgobj = pathstr
|
|
||||||
} else {
|
|
||||||
pkgobj = pathstr[:dot]
|
|
||||||
suffix = pathstr[dot:] // suffix starts with "."
|
|
||||||
}
|
|
||||||
|
|
||||||
obj := pkg.Scope().Lookup(pkgobj)
|
|
||||||
if obj == nil {
|
|
||||||
return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// abstraction of *types.{Pointer,Slice,Array,Chan,Map}
|
|
||||||
type hasElem interface {
|
|
||||||
Elem() types.Type
|
|
||||||
}
|
|
||||||
// abstraction of *types.{Named,Signature}
|
|
||||||
type hasTypeParams interface {
|
|
||||||
TypeParams() *typeparams.TypeParamList
|
|
||||||
}
|
|
||||||
// abstraction of *types.{Named,TypeParam}
|
|
||||||
type hasObj interface {
|
|
||||||
Obj() *types.TypeName
|
|
||||||
}
|
|
||||||
|
|
||||||
// The loop state is the pair (t, obj),
|
|
||||||
// exactly one of which is non-nil, initially obj.
|
|
||||||
// All suffixes start with '.' (the only object->type operation),
|
|
||||||
// followed by optional type->type operations,
|
|
||||||
// then a type->object operation.
|
|
||||||
// The cycle then repeats.
|
|
||||||
var t types.Type
|
|
||||||
for suffix != "" {
|
|
||||||
code := suffix[0]
|
|
||||||
suffix = suffix[1:]
|
|
||||||
|
|
||||||
// Codes [AFM] have an integer operand.
|
|
||||||
var index int
|
|
||||||
switch code {
|
|
||||||
case opAt, opField, opMethod, opTypeParam:
|
|
||||||
rest := strings.TrimLeft(suffix, "0123456789")
|
|
||||||
numerals := suffix[:len(suffix)-len(rest)]
|
|
||||||
suffix = rest
|
|
||||||
i, err := strconv.Atoi(numerals)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code)
|
|
||||||
}
|
|
||||||
index = int(i)
|
|
||||||
case opObj:
|
|
||||||
// no operand
|
|
||||||
default:
|
|
||||||
// The suffix must end with a type->object operation.
|
|
||||||
if suffix == "" {
|
|
||||||
return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if code == opType {
|
|
||||||
if t != nil {
|
|
||||||
return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType)
|
|
||||||
}
|
|
||||||
t = obj.Type()
|
|
||||||
obj = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if t == nil {
|
|
||||||
return nil, fmt.Errorf("invalid path: code %q in object context", code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inv: t != nil, obj == nil
|
|
||||||
|
|
||||||
switch code {
|
|
||||||
case opElem:
|
|
||||||
hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t)
|
|
||||||
}
|
|
||||||
t = hasElem.Elem()
|
|
||||||
|
|
||||||
case opKey:
|
|
||||||
mapType, ok := t.(*types.Map)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t)
|
|
||||||
}
|
|
||||||
t = mapType.Key()
|
|
||||||
|
|
||||||
case opParams:
|
|
||||||
sig, ok := t.(*types.Signature)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
|
|
||||||
}
|
|
||||||
t = sig.Params()
|
|
||||||
|
|
||||||
case opResults:
|
|
||||||
sig, ok := t.(*types.Signature)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
|
|
||||||
}
|
|
||||||
t = sig.Results()
|
|
||||||
|
|
||||||
case opUnderlying:
|
|
||||||
named, ok := t.(*types.Named)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t)
|
|
||||||
}
|
|
||||||
t = named.Underlying()
|
|
||||||
|
|
||||||
case opTypeParam:
|
|
||||||
hasTypeParams, ok := t.(hasTypeParams) // Named, Signature
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t)
|
|
||||||
}
|
|
||||||
tparams := hasTypeParams.TypeParams()
|
|
||||||
if n := tparams.Len(); index >= n {
|
|
||||||
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
|
|
||||||
}
|
|
||||||
t = tparams.At(index)
|
|
||||||
|
|
||||||
case opConstraint:
|
|
||||||
tparam, ok := t.(*typeparams.TypeParam)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t)
|
|
||||||
}
|
|
||||||
t = tparam.Constraint()
|
|
||||||
|
|
||||||
case opAt:
|
|
||||||
tuple, ok := t.(*types.Tuple)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t)
|
|
||||||
}
|
|
||||||
if n := tuple.Len(); index >= n {
|
|
||||||
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
|
|
||||||
}
|
|
||||||
obj = tuple.At(index)
|
|
||||||
t = nil
|
|
||||||
|
|
||||||
case opField:
|
|
||||||
structType, ok := t.(*types.Struct)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t)
|
|
||||||
}
|
|
||||||
if n := structType.NumFields(); index >= n {
|
|
||||||
return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n)
|
|
||||||
}
|
|
||||||
obj = structType.Field(index)
|
|
||||||
t = nil
|
|
||||||
|
|
||||||
case opMethod:
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.Interface:
|
|
||||||
if index >= t.NumMethods() {
|
|
||||||
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
|
|
||||||
}
|
|
||||||
obj = t.Method(index) // Id-ordered
|
|
||||||
|
|
||||||
case *types.Named:
|
|
||||||
if index >= t.NumMethods() {
|
|
||||||
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
|
|
||||||
}
|
|
||||||
obj = t.Method(index)
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t)
|
|
||||||
}
|
|
||||||
t = nil
|
|
||||||
|
|
||||||
case opObj:
|
|
||||||
hasObj, ok := t.(hasObj)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t)
|
|
||||||
}
|
|
||||||
obj = hasObj.Obj()
|
|
||||||
t = nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid path: unknown code %q", code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj.Pkg() != pkg {
|
|
||||||
return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj, nil // success
|
|
||||||
}
|
|
||||||
|
|
||||||
// scopeObjects is a memoization of scope objects.
|
|
||||||
// Callers must not modify the result.
|
|
||||||
func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object {
|
|
||||||
m := enc.scopeMemo
|
|
||||||
if m == nil {
|
|
||||||
m = make(map[*types.Scope][]types.Object)
|
|
||||||
enc.scopeMemo = m
|
|
||||||
}
|
|
||||||
objs, ok := m[scope]
|
|
||||||
if !ok {
|
|
||||||
names := scope.Names() // allocates and sorts
|
|
||||||
objs = make([]types.Object, len(names))
|
|
||||||
for i, name := range names {
|
|
||||||
objs[i] = scope.Lookup(name)
|
|
||||||
}
|
|
||||||
m[scope] = objs
|
|
||||||
}
|
|
||||||
return objs
|
|
||||||
}
|
|
||||||
-85
@@ -1,85 +0,0 @@
|
|||||||
// Copyright 2019 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.
|
|
||||||
|
|
||||||
// Package core provides support for event based telemetry.
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/event/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Event holds the information about an event of note that occurred.
|
|
||||||
type Event struct {
|
|
||||||
at time.Time
|
|
||||||
|
|
||||||
// As events are often on the stack, storing the first few labels directly
|
|
||||||
// in the event can avoid an allocation at all for the very common cases of
|
|
||||||
// simple events.
|
|
||||||
// The length needs to be large enough to cope with the majority of events
|
|
||||||
// but no so large as to cause undue stack pressure.
|
|
||||||
// A log message with two values will use 3 labels (one for each value and
|
|
||||||
// one for the message itself).
|
|
||||||
|
|
||||||
static [3]label.Label // inline storage for the first few labels
|
|
||||||
dynamic []label.Label // dynamically sized storage for remaining labels
|
|
||||||
}
|
|
||||||
|
|
||||||
// eventLabelMap implements label.Map for a the labels of an Event.
|
|
||||||
type eventLabelMap struct {
|
|
||||||
event Event
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ev Event) At() time.Time { return ev.at }
|
|
||||||
|
|
||||||
func (ev Event) Format(f fmt.State, r rune) {
|
|
||||||
if !ev.at.IsZero() {
|
|
||||||
fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 "))
|
|
||||||
}
|
|
||||||
for index := 0; ev.Valid(index); index++ {
|
|
||||||
if l := ev.Label(index); l.Valid() {
|
|
||||||
fmt.Fprintf(f, "\n\t%v", l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ev Event) Valid(index int) bool {
|
|
||||||
return index >= 0 && index < len(ev.static)+len(ev.dynamic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ev Event) Label(index int) label.Label {
|
|
||||||
if index < len(ev.static) {
|
|
||||||
return ev.static[index]
|
|
||||||
}
|
|
||||||
return ev.dynamic[index-len(ev.static)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ev Event) Find(key label.Key) label.Label {
|
|
||||||
for _, l := range ev.static {
|
|
||||||
if l.Key() == key {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, l := range ev.dynamic {
|
|
||||||
if l.Key() == key {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return label.Label{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeEvent(static [3]label.Label, labels []label.Label) Event {
|
|
||||||
return Event{
|
|
||||||
static: static,
|
|
||||||
dynamic: labels,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloneEvent event returns a copy of the event with the time adjusted to at.
|
|
||||||
func CloneEvent(ev Event, at time.Time) Event {
|
|
||||||
ev.at = at
|
|
||||||
return ev
|
|
||||||
}
|
|
||||||
-70
@@ -1,70 +0,0 @@
|
|||||||
// Copyright 2019 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.
|
|
||||||
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/event/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Exporter is a function that handles events.
|
|
||||||
// It may return a modified context and event.
|
|
||||||
type Exporter func(context.Context, Event, label.Map) context.Context
|
|
||||||
|
|
||||||
var (
|
|
||||||
exporter unsafe.Pointer
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetExporter sets the global exporter function that handles all events.
|
|
||||||
// The exporter is called synchronously from the event call site, so it should
|
|
||||||
// return quickly so as not to hold up user code.
|
|
||||||
func SetExporter(e Exporter) {
|
|
||||||
p := unsafe.Pointer(&e)
|
|
||||||
if e == nil {
|
|
||||||
// &e is always valid, and so p is always valid, but for the early abort
|
|
||||||
// of ProcessEvent to be efficient it needs to make the nil check on the
|
|
||||||
// pointer without having to dereference it, so we make the nil function
|
|
||||||
// also a nil pointer
|
|
||||||
p = nil
|
|
||||||
}
|
|
||||||
atomic.StorePointer(&exporter, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// deliver is called to deliver an event to the supplied exporter.
|
|
||||||
// it will fill in the time.
|
|
||||||
func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context {
|
|
||||||
// add the current time to the event
|
|
||||||
ev.at = time.Now()
|
|
||||||
// hand the event off to the current exporter
|
|
||||||
return exporter(ctx, ev, ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export is called to deliver an event to the global exporter if set.
|
|
||||||
func Export(ctx context.Context, ev Event) context.Context {
|
|
||||||
// get the global exporter and abort early if there is not one
|
|
||||||
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
|
|
||||||
if exporterPtr == nil {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
return deliver(ctx, *exporterPtr, ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportPair is called to deliver a start event to the supplied exporter.
|
|
||||||
// It also returns a function that will deliver the end event to the same
|
|
||||||
// exporter.
|
|
||||||
// It will fill in the time.
|
|
||||||
func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) {
|
|
||||||
// get the global exporter and abort early if there is not one
|
|
||||||
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
|
|
||||||
if exporterPtr == nil {
|
|
||||||
return ctx, func() {}
|
|
||||||
}
|
|
||||||
ctx = deliver(ctx, *exporterPtr, begin)
|
|
||||||
return ctx, func() { deliver(ctx, *exporterPtr, end) }
|
|
||||||
}
|
|
||||||
-77
@@ -1,77 +0,0 @@
|
|||||||
// Copyright 2019 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.
|
|
||||||
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/event/keys"
|
|
||||||
"golang.org/x/tools/internal/event/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Log1 takes a message and one label delivers a log event to the exporter.
|
|
||||||
// It is a customized version of Print that is faster and does no allocation.
|
|
||||||
func Log1(ctx context.Context, message string, t1 label.Label) {
|
|
||||||
Export(ctx, MakeEvent([3]label.Label{
|
|
||||||
keys.Msg.Of(message),
|
|
||||||
t1,
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log2 takes a message and two labels and delivers a log event to the exporter.
|
|
||||||
// It is a customized version of Print that is faster and does no allocation.
|
|
||||||
func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) {
|
|
||||||
Export(ctx, MakeEvent([3]label.Label{
|
|
||||||
keys.Msg.Of(message),
|
|
||||||
t1,
|
|
||||||
t2,
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metric1 sends a label event to the exporter with the supplied labels.
|
|
||||||
func Metric1(ctx context.Context, t1 label.Label) context.Context {
|
|
||||||
return Export(ctx, MakeEvent([3]label.Label{
|
|
||||||
keys.Metric.New(),
|
|
||||||
t1,
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metric2 sends a label event to the exporter with the supplied labels.
|
|
||||||
func Metric2(ctx context.Context, t1, t2 label.Label) context.Context {
|
|
||||||
return Export(ctx, MakeEvent([3]label.Label{
|
|
||||||
keys.Metric.New(),
|
|
||||||
t1,
|
|
||||||
t2,
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start1 sends a span start event with the supplied label list to the exporter.
|
|
||||||
// It also returns a function that will end the span, which should normally be
|
|
||||||
// deferred.
|
|
||||||
func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) {
|
|
||||||
return ExportPair(ctx,
|
|
||||||
MakeEvent([3]label.Label{
|
|
||||||
keys.Start.Of(name),
|
|
||||||
t1,
|
|
||||||
}, nil),
|
|
||||||
MakeEvent([3]label.Label{
|
|
||||||
keys.End.New(),
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start2 sends a span start event with the supplied label list to the exporter.
|
|
||||||
// It also returns a function that will end the span, which should normally be
|
|
||||||
// deferred.
|
|
||||||
func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) {
|
|
||||||
return ExportPair(ctx,
|
|
||||||
MakeEvent([3]label.Label{
|
|
||||||
keys.Start.Of(name),
|
|
||||||
t1,
|
|
||||||
t2,
|
|
||||||
}, nil),
|
|
||||||
MakeEvent([3]label.Label{
|
|
||||||
keys.End.New(),
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
-7
@@ -1,7 +0,0 @@
|
|||||||
// Copyright 2019 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.
|
|
||||||
|
|
||||||
// Package event provides a set of packages that cover the main
|
|
||||||
// concepts of telemetry in an implementation agnostic way.
|
|
||||||
package event
|
|
||||||
-127
@@ -1,127 +0,0 @@
|
|||||||
// Copyright 2019 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.
|
|
||||||
|
|
||||||
package event
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/event/core"
|
|
||||||
"golang.org/x/tools/internal/event/keys"
|
|
||||||
"golang.org/x/tools/internal/event/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Exporter is a function that handles events.
|
|
||||||
// It may return a modified context and event.
|
|
||||||
type Exporter func(context.Context, core.Event, label.Map) context.Context
|
|
||||||
|
|
||||||
// SetExporter sets the global exporter function that handles all events.
|
|
||||||
// The exporter is called synchronously from the event call site, so it should
|
|
||||||
// return quickly so as not to hold up user code.
|
|
||||||
func SetExporter(e Exporter) {
|
|
||||||
core.SetExporter(core.Exporter(e))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log takes a message and a label list and combines them into a single event
|
|
||||||
// before delivering them to the exporter.
|
|
||||||
func Log(ctx context.Context, message string, labels ...label.Label) {
|
|
||||||
core.Export(ctx, core.MakeEvent([3]label.Label{
|
|
||||||
keys.Msg.Of(message),
|
|
||||||
}, labels))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLog returns true if the event was built by the Log function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsLog(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Msg
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error takes a message and a label list and combines them into a single event
|
|
||||||
// before delivering them to the exporter. It captures the error in the
|
|
||||||
// delivered event.
|
|
||||||
func Error(ctx context.Context, message string, err error, labels ...label.Label) {
|
|
||||||
core.Export(ctx, core.MakeEvent([3]label.Label{
|
|
||||||
keys.Msg.Of(message),
|
|
||||||
keys.Err.Of(err),
|
|
||||||
}, labels))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsError returns true if the event was built by the Error function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsError(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Msg &&
|
|
||||||
ev.Label(1).Key() == keys.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metric sends a label event to the exporter with the supplied labels.
|
|
||||||
func Metric(ctx context.Context, labels ...label.Label) {
|
|
||||||
core.Export(ctx, core.MakeEvent([3]label.Label{
|
|
||||||
keys.Metric.New(),
|
|
||||||
}, labels))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsMetric returns true if the event was built by the Metric function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsMetric(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Metric
|
|
||||||
}
|
|
||||||
|
|
||||||
// Label sends a label event to the exporter with the supplied labels.
|
|
||||||
func Label(ctx context.Context, labels ...label.Label) context.Context {
|
|
||||||
return core.Export(ctx, core.MakeEvent([3]label.Label{
|
|
||||||
keys.Label.New(),
|
|
||||||
}, labels))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLabel returns true if the event was built by the Label function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsLabel(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Label
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start sends a span start event with the supplied label list to the exporter.
|
|
||||||
// It also returns a function that will end the span, which should normally be
|
|
||||||
// deferred.
|
|
||||||
func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) {
|
|
||||||
return core.ExportPair(ctx,
|
|
||||||
core.MakeEvent([3]label.Label{
|
|
||||||
keys.Start.Of(name),
|
|
||||||
}, labels),
|
|
||||||
core.MakeEvent([3]label.Label{
|
|
||||||
keys.End.New(),
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsStart returns true if the event was built by the Start function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsStart(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Start
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEnd returns true if the event was built by the End function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsEnd(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.End
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detach returns a context without an associated span.
|
|
||||||
// This allows the creation of spans that are not children of the current span.
|
|
||||||
func Detach(ctx context.Context) context.Context {
|
|
||||||
return core.Export(ctx, core.MakeEvent([3]label.Label{
|
|
||||||
keys.Detach.New(),
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDetach returns true if the event was built by the Detach function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsDetach(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Detach
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user