Compare commits
151 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
8d85ad5269 | 2 years ago |
|
|
008ce88567 | 2 years ago |
|
|
17a7e99226 | 2 years ago |
|
|
027a8c16e7 | 2 years ago |
|
|
a12bc79097 | 2 years ago |
|
|
a0b5a420e8 | 2 years ago |
|
|
0151ea9a3f | 2 years ago |
|
|
221d091b68 | 2 years ago |
|
|
1760b8b586 | 2 years ago |
|
|
4d142d8b45 | 2 years ago |
|
|
44dd1f1f5e | 2 years ago |
|
|
f35b2b7cab | 2 years ago |
|
|
29ba5ecef6 | 2 years ago |
|
|
87e8e4b847 | 2 years ago |
|
|
a71a24c0f4 | 2 years ago |
|
|
76119b0f61 | 2 years ago |
|
|
7843b5f417 | 2 years ago |
|
|
da6662975f | 2 years ago |
|
|
de4dbb7d00 | 2 years ago |
|
|
3bd4bca994 | 2 years ago |
|
|
296832c90e | 2 years ago |
|
|
56d55a4137 | 2 years ago |
|
|
626e6f8fa3 | 2 years ago |
|
|
5941bf0494 | 2 years ago |
|
|
29a496cdab | 2 years ago |
|
|
a43d9a67c7 | 2 years ago |
|
|
c47eb3bf5a | 2 years ago |
|
|
a97e1641a4 | 2 years ago |
|
|
86ae8ea854 | 2 years ago |
|
|
d37d483097 | 2 years ago |
|
|
4e96faa201 | 2 years ago |
|
|
e5419ef6d7 | 2 years ago |
|
|
14747a490a | 2 years ago |
|
|
e5cee892ed | 2 years ago |
|
|
ef4b984df4 | 2 years ago |
|
|
a8f402e28d | 2 years ago |
|
|
2eba99b40b | 2 years ago |
|
|
7686fa1f16 | 2 years ago |
|
|
51b9bab245 | 2 years ago |
|
|
6b5758f4cd | 2 years ago |
|
|
bd375a14a8 | 2 years ago |
|
|
b01693f63e | 2 years ago |
|
|
4a059d5144 | 2 years ago |
|
|
f3775c0046 | 2 years ago |
|
|
50fbdd86f9 | 2 years ago |
|
|
1f61de0fcc | 2 years ago |
|
|
e206c585bb | 2 years ago |
|
|
5e46d8057d | 2 years ago |
|
|
4e7709e54c | 2 years ago |
|
|
5ed8f1b7d9 | 2 years ago |
|
|
1d12c1f5b3 | 2 years ago |
|
|
3ef93e081c | 2 years ago |
|
|
18894a8e3a | 2 years ago |
|
|
13ec635988 | 2 years ago |
|
|
f804b8fa4b | 2 years ago |
|
|
21a55ff9a1 | 2 years ago |
|
|
dd350284df | 2 years ago |
|
|
c010d3de8d | 2 years ago |
|
|
d11dbbf9f7 | 2 years ago |
|
|
75cdceb9f1 | 2 years ago |
|
|
10ff93f190 | 2 years ago |
|
|
bf00185809 | 2 years ago |
|
|
90f03e57c2 | 2 years ago |
|
|
a59fd3ebfe | 2 years ago |
|
|
3eb490153d | 2 years ago |
|
|
d957d8b987 | 2 years ago |
|
|
5a1f252bd9 | 2 years ago |
|
|
ab4585f38c | 2 years ago |
|
|
3003045c0b | 2 years ago |
|
|
a6f3f290b4 | 2 years ago |
|
|
27d072a099 | 2 years ago |
|
|
8e3df1943c | 2 years ago |
|
|
8c54de66ce | 2 years ago |
|
|
06b9ac2dc4 | 2 years ago |
|
|
b8739d7441 | 2 years ago |
|
|
23fe02993b | 2 years ago |
|
|
1d177f00d2 | 2 years ago |
|
|
ceaba7011f | 2 years ago |
|
|
9c06f383ba | 2 years ago |
|
|
e11c5e3e96 | 2 years ago |
|
|
f5719f3017 | 2 years ago |
|
|
163babdca7 | 2 years ago |
|
|
094d1aded8 | 2 years ago |
|
|
05ef20b434 | 2 years ago |
|
|
cc718b3444 | 2 years ago |
|
|
e98e8f6ac9 | 2 years ago |
|
|
36541ed9d5 | 2 years ago |
|
|
418ea82d3a | 2 years ago |
|
|
130bbda00e | 2 years ago |
|
|
2666bd6996 | 2 years ago |
|
|
ff2c8da803 | 2 years ago |
|
|
e094296f37 | 2 years ago |
|
|
7c3b77fb36 | 2 years ago |
|
|
fb4c4f07ca | 2 years ago |
|
|
b9e25e82cf | 2 years ago |
|
|
089036da29 | 2 years ago |
|
|
1123bfed10 | 2 years ago |
|
|
7f2293308b | 2 years ago |
|
|
a65131f9d3 | 2 years ago |
|
|
8a3a646c61 | 2 years ago |
|
|
4384947be1 | 2 years ago |
|
|
69421182ca | 2 years ago |
|
|
068382f5df | 2 years ago |
|
|
c4bec05466 | 2 years ago |
|
|
89e1ac0a6e | 2 years ago |
|
|
b84e0e11b4 | 2 years ago |
|
|
d95f5f8f3b | 2 years ago |
|
|
b4c0941683 | 2 years ago |
|
|
cf9798cede | 2 years ago |
|
|
20d2501edc | 2 years ago |
|
|
d45601fdc6 | 2 years ago |
|
|
c81a9a89cf | 2 years ago |
|
|
87b9f9ecfb | 2 years ago |
|
|
cbc473359a | 2 years ago |
|
|
2eba60db75 | 2 years ago |
|
|
0dcbed3f53 | 2 years ago |
|
|
ca08eb65e2 | 2 years ago |
|
|
6f37d9bee7 | 2 years ago |
|
|
e65f6b8c8b | 2 years ago |
|
|
707dc43d55 | 2 years ago |
|
|
8cbb7a9319 | 2 years ago |
|
|
4f5a56aadb | 2 years ago |
|
|
399beb53d9 | 2 years ago |
|
|
7dec9fd6e7 | 2 years ago |
|
|
120f3a8918 | 2 years ago |
|
|
bd672eaf5b | 2 years ago |
|
|
c2500ea2d8 | 2 years ago |
|
|
a4663b4b2e | 2 years ago |
|
|
57c618b83a | 2 years ago |
|
|
b3a4f95110 | 2 years ago |
|
|
28a1eb3527 | 3 years ago |
|
|
75ecc15958 | 3 years ago |
|
|
2235ebce2f | 3 years ago |
|
|
7147463418 | 3 years ago |
|
|
010e4c8d54 | 3 years ago |
|
|
6f394a0691 | 3 years ago |
|
|
efd7279118 | 3 years ago |
|
|
601056f3a7 | 3 years ago |
|
|
0a7f96cbfb | 3 years ago |
|
|
1c530c2fe0 | 3 years ago |
|
|
1e576dd7c6 | 3 years ago |
|
|
7a5472153b | 3 years ago |
|
|
b986ce566b | 3 years ago |
|
|
daba16f4be | 3 years ago |
|
|
ee36e2264e | 3 years ago |
|
|
329e98d9f0 | 3 years ago |
|
|
f4513f7028 | 3 years ago |
|
|
b1c5449428 | 3 years ago |
|
|
431732f5d1 | 3 years ago |
|
|
d0bff18cee | 3 years ago |
|
|
8ad30d0a35 | 3 years ago |
@ -0,0 +1,25 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/go
|
||||||
|
{
|
||||||
|
"name": "Go",
|
||||||
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/go:1-1.21-bullseye",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
// "features": {},
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
// "postCreateCommand": "go version",
|
||||||
|
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
// "customizations": {},
|
||||||
|
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "root"
|
||||||
|
}
|
||||||
Binary file not shown.
@ -0,0 +1,267 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/platforms"
|
||||||
|
"github.com/containerd/continuity/fs/fstest"
|
||||||
|
"github.com/moby/buildkit/util/testutil/integration"
|
||||||
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var imagetoolsTests = []func(t *testing.T, sb integration.Sandbox){
|
||||||
|
testImagetoolsCopyManifest,
|
||||||
|
testImagetoolsCopyIndex,
|
||||||
|
testImagetoolsInspectAndFilter,
|
||||||
|
testImagetoolsAnnotation,
|
||||||
|
}
|
||||||
|
|
||||||
|
func testImagetoolsCopyManifest(t *testing.T, sb integration.Sandbox) {
|
||||||
|
if sb.Name() != "docker-container" {
|
||||||
|
t.Skip("imagetools tests are not driver specific and only run on docker-container")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := createDockerfile(t)
|
||||||
|
registry, err := sb.NewRegistry()
|
||||||
|
if errors.Is(err, integration.ErrRequirements) {
|
||||||
|
t.Skip(err.Error())
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
target := registry + "/buildx/imtools-manifest:latest"
|
||||||
|
|
||||||
|
out, err := buildCmd(sb, withArgs("-t", target, "--push", "--platform=linux/amd64", "--provenance=false", dir))
|
||||||
|
require.NoError(t, err, string(out))
|
||||||
|
|
||||||
|
cmd := buildxCmd(sb, withArgs("imagetools", "inspect", target, "--raw"))
|
||||||
|
dt, err := cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
var mfst ocispecs.Manifest
|
||||||
|
err = json.Unmarshal(dt, &mfst)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, images.MediaTypeDockerSchema2Manifest, mfst.MediaType)
|
||||||
|
|
||||||
|
registry2, err := sb.NewRegistry()
|
||||||
|
require.NoError(t, err)
|
||||||
|
target2 := registry2 + "/buildx/imtools2-manifest:latest"
|
||||||
|
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "create", "-t", target2, target))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "inspect", target2, "--raw"))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
var idx2 ocispecs.Index
|
||||||
|
err = json.Unmarshal(dt, &idx2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, images.MediaTypeDockerSchema2ManifestList, idx2.MediaType)
|
||||||
|
require.Equal(t, 1, len(idx2.Manifests))
|
||||||
|
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "inspect", target2+"@"+string(idx2.Manifests[0].Digest), "--raw"))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
var mfst2 ocispecs.Manifest
|
||||||
|
err = json.Unmarshal(dt, &mfst2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, images.MediaTypeDockerSchema2Manifest, mfst2.MediaType)
|
||||||
|
|
||||||
|
require.Equal(t, mfst.Config.Digest, mfst2.Config.Digest)
|
||||||
|
require.Equal(t, len(mfst.Layers), len(mfst2.Layers))
|
||||||
|
for i := range mfst.Layers {
|
||||||
|
require.Equal(t, mfst.Layers[i].Digest, mfst2.Layers[i].Digest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testImagetoolsCopyIndex(t *testing.T, sb integration.Sandbox) {
|
||||||
|
if sb.Name() != "docker-container" {
|
||||||
|
t.Skip("imagetools tests are not driver specific and only run on docker-container")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := createDockerfile(t)
|
||||||
|
registry, err := sb.NewRegistry()
|
||||||
|
if errors.Is(err, integration.ErrRequirements) {
|
||||||
|
t.Skip(err.Error())
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
target := registry + "/buildx/imtools:latest"
|
||||||
|
|
||||||
|
out, err := buildCmd(sb, withArgs("-t", target, "--push", "--platform=linux/amd64,linux/arm64", "--provenance=false", dir))
|
||||||
|
require.NoError(t, err, string(out))
|
||||||
|
|
||||||
|
cmd := buildxCmd(sb, withArgs("imagetools", "inspect", target, "--raw"))
|
||||||
|
dt, err := cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
var idx ocispecs.Index
|
||||||
|
err = json.Unmarshal(dt, &idx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, images.MediaTypeDockerSchema2ManifestList, idx.MediaType)
|
||||||
|
require.Equal(t, 2, len(idx.Manifests))
|
||||||
|
|
||||||
|
registry2, err := sb.NewRegistry()
|
||||||
|
require.NoError(t, err)
|
||||||
|
target2 := registry2 + "/buildx/imtools2:latest"
|
||||||
|
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "create", "-t", target2, target))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "inspect", target2, "--raw"))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
var idx2 ocispecs.Index
|
||||||
|
err = json.Unmarshal(dt, &idx2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, images.MediaTypeDockerSchema2ManifestList, idx2.MediaType)
|
||||||
|
|
||||||
|
require.Equal(t, len(idx.Manifests), len(idx2.Manifests))
|
||||||
|
for i := range idx.Manifests {
|
||||||
|
require.Equal(t, idx.Manifests[i].Digest, idx2.Manifests[i].Digest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testImagetoolsInspectAndFilter(t *testing.T, sb integration.Sandbox) {
|
||||||
|
if sb.Name() != "docker-container" {
|
||||||
|
t.Skip("imagetools tests are not driver specific and only run on docker-container")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := createDockerfile(t)
|
||||||
|
registry, err := sb.NewRegistry()
|
||||||
|
if errors.Is(err, integration.ErrRequirements) {
|
||||||
|
t.Skip(err.Error())
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
target := registry + "/buildx/imtools:latest"
|
||||||
|
|
||||||
|
out, err := buildCmd(sb, withArgs("-t", target, "--push", "--platform=linux/amd64,linux/arm64", "--provenance=false", dir))
|
||||||
|
require.NoError(t, err, string(out))
|
||||||
|
|
||||||
|
cmd := buildxCmd(sb, withArgs("imagetools", "inspect", target, "--raw"))
|
||||||
|
dt, err := cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
var idx ocispecs.Index
|
||||||
|
err = json.Unmarshal(dt, &idx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, 2, len(idx.Manifests))
|
||||||
|
|
||||||
|
mfst := idx.Manifests[0]
|
||||||
|
require.Equal(t, "linux/amd64", platforms.Format(*mfst.Platform))
|
||||||
|
|
||||||
|
mfst = idx.Manifests[1]
|
||||||
|
require.Equal(t, "linux/arm64", platforms.Format(*mfst.Platform))
|
||||||
|
|
||||||
|
// create amd64 only image
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "create", "-t", target+"-arm64", target+"@"+string(idx.Manifests[1].Digest)))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "inspect", target+"-arm64", "--raw"))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
var idx2 ocispecs.Index
|
||||||
|
err = json.Unmarshal(dt, &idx2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, 1, len(idx2.Manifests))
|
||||||
|
|
||||||
|
require.Equal(t, idx.Manifests[1].Digest, idx2.Manifests[0].Digest)
|
||||||
|
require.Equal(t, platforms.Format(*idx.Manifests[1].Platform), platforms.Format(*idx2.Manifests[0].Platform))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testImagetoolsAnnotation(t *testing.T, sb integration.Sandbox) {
|
||||||
|
if sb.Name() != "docker-container" {
|
||||||
|
t.Skip("imagetools tests are not driver specific and only run on docker-container")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := createDockerfile(t)
|
||||||
|
registry, err := sb.NewRegistry()
|
||||||
|
if errors.Is(err, integration.ErrRequirements) {
|
||||||
|
t.Skip(err.Error())
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
target := registry + "/buildx/imtools:latest"
|
||||||
|
|
||||||
|
out, err := buildCmd(sb, withArgs("--output", "type=registry,oci-mediatypes=true,name="+target, "--platform=linux/amd64,linux/arm64", "--provenance=false", dir))
|
||||||
|
require.NoError(t, err, string(out))
|
||||||
|
|
||||||
|
cmd := buildxCmd(sb, withArgs("imagetools", "inspect", target, "--raw"))
|
||||||
|
dt, err := cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
var idx ocispecs.Index
|
||||||
|
err = json.Unmarshal(dt, &idx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, idx.Annotations)
|
||||||
|
|
||||||
|
imagetoolsCmd := func(source []string) *exec.Cmd {
|
||||||
|
args := []string{"imagetools", "create", "-t", target, "--annotation", "index:foo=bar", "--annotation", "index:bar=baz",
|
||||||
|
"--annotation", "manifest-descriptor:foo=bar", "--annotation", "manifest-descriptor[linux/amd64]:bar=baz"}
|
||||||
|
args = append(args, source...)
|
||||||
|
return buildxCmd(sb, withArgs(args...))
|
||||||
|
}
|
||||||
|
sources := [][]string{
|
||||||
|
{
|
||||||
|
target,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target + "@" + string(idx.Manifests[0].Digest),
|
||||||
|
target + "@" + string(idx.Manifests[1].Digest),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, source := range sources {
|
||||||
|
cmd = imagetoolsCmd(source)
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
newTarget := registry + "/buildx/imtools:annotations"
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "create", "-t", newTarget, target))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "inspect", newTarget, "--raw"))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
err = json.Unmarshal(dt, &idx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, idx.Annotations, 2)
|
||||||
|
require.Equal(t, "bar", idx.Annotations["foo"])
|
||||||
|
require.Equal(t, "baz", idx.Annotations["bar"])
|
||||||
|
require.Len(t, idx.Manifests, 2)
|
||||||
|
for _, mfst := range idx.Manifests {
|
||||||
|
require.Equal(t, "bar", mfst.Annotations["foo"])
|
||||||
|
if platforms.Format(*mfst.Platform) == "linux/amd64" {
|
||||||
|
require.Equal(t, "baz", mfst.Annotations["bar"])
|
||||||
|
} else {
|
||||||
|
require.Empty(t, mfst.Annotations["bar"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDockerfile(t *testing.T) string {
|
||||||
|
dockerfile := []byte(`
|
||||||
|
FROM scratch
|
||||||
|
ARG TARGETARCH
|
||||||
|
COPY foo-${TARGETARCH} /foo
|
||||||
|
`)
|
||||||
|
dir := tmpdir(
|
||||||
|
t,
|
||||||
|
fstest.CreateFile("Dockerfile", dockerfile, 0600),
|
||||||
|
fstest.CreateFile("foo-amd64", []byte("foo-amd64"), 0600),
|
||||||
|
fstest.CreateFile("foo-arm64", []byte("foo-arm64"), 0600),
|
||||||
|
)
|
||||||
|
return dir
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/moby/buildkit/util/testutil/integration"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/mod/module"
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
var versionTests = []func(t *testing.T, sb integration.Sandbox){
|
||||||
|
testVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
func testVersion(t *testing.T, sb integration.Sandbox) {
|
||||||
|
cmd := buildxCmd(sb, withArgs("version"))
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(out))
|
||||||
|
|
||||||
|
// There should be at least one newline and the first line
|
||||||
|
// of output should contain the name, version, and possibly a revision.
|
||||||
|
firstLine, _, hasNewline := strings.Cut(string(out), "\n")
|
||||||
|
require.True(t, hasNewline, "At least one newline is required in the output")
|
||||||
|
|
||||||
|
// Log the output to make debugging easier.
|
||||||
|
t.Log(firstLine)
|
||||||
|
|
||||||
|
// Split by spaces into at least 2 fields.
|
||||||
|
fields := strings.Fields(firstLine)
|
||||||
|
require.GreaterOrEqual(t, len(fields), 2, "Expected at least 2 fields in the first line")
|
||||||
|
|
||||||
|
// First field should be an import path.
|
||||||
|
// This can be any valid import path for Go
|
||||||
|
// so don't set too many restrictions here.
|
||||||
|
// Just checking if the import path is a valid Go
|
||||||
|
// path should be suitable enough to make sure this is ok.
|
||||||
|
// Using CheckImportPath instead of CheckPath as it is less
|
||||||
|
// restrictive.
|
||||||
|
importPath := fields[0]
|
||||||
|
require.NoError(t, module.CheckImportPath(importPath), "First field was not a valid import path: %+v", importPath)
|
||||||
|
|
||||||
|
// Second field should be a version.
|
||||||
|
// This defaults to something that's still compatible
|
||||||
|
// with semver.
|
||||||
|
version := fields[1]
|
||||||
|
require.True(t, semver.IsValid(version), "Second field was not valid semver: %+v", version)
|
||||||
|
|
||||||
|
// Revision should be empty or should look like a git hash.
|
||||||
|
if len(fields) > 2 && len(fields[2]) > 0 {
|
||||||
|
revision := fields[2]
|
||||||
|
require.Regexp(t, `[0-9a-f]{40}`, revision, "Third field was not a git revision: %+v", revision)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package workers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/moby/buildkit/util/testutil/integration"
|
||||||
|
)
|
||||||
|
|
||||||
|
var features = map[string]struct{}{}
|
||||||
|
|
||||||
|
func CheckFeatureCompat(t *testing.T, sb integration.Sandbox, reason ...string) {
|
||||||
|
integration.CheckFeatureCompat(t, sb, features, reason...)
|
||||||
|
}
|
||||||
@ -1,21 +1,15 @@
|
|||||||
package buildflags
|
package buildflags
|
||||||
|
|
||||||
import (
|
import "github.com/moby/buildkit/util/entitlements"
|
||||||
"github.com/moby/buildkit/util/entitlements"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseEntitlements(in []string) ([]entitlements.Entitlement, error) {
|
func ParseEntitlements(in []string) ([]entitlements.Entitlement, error) {
|
||||||
out := make([]entitlements.Entitlement, 0, len(in))
|
out := make([]entitlements.Entitlement, 0, len(in))
|
||||||
for _, v := range in {
|
for _, v := range in {
|
||||||
switch v {
|
e, err := entitlements.Parse(v)
|
||||||
case "security.insecure":
|
if err != nil {
|
||||||
out = append(out, entitlements.EntitlementSecurityInsecure)
|
return nil, err
|
||||||
case "network.host":
|
|
||||||
out = append(out, entitlements.EntitlementNetworkHost)
|
|
||||||
default:
|
|
||||||
return nil, errors.Errorf("invalid entitlement: %v", v)
|
|
||||||
}
|
}
|
||||||
|
out = append(out, e)
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Compose Specification Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dotenv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetEnvFromFile(currentEnv map[string]string, workingDir string, filenames []string) (map[string]string, error) {
|
||||||
|
envMap := make(map[string]string)
|
||||||
|
|
||||||
|
dotEnvFiles := filenames
|
||||||
|
if len(dotEnvFiles) == 0 {
|
||||||
|
dotEnvFiles = append(dotEnvFiles, filepath.Join(workingDir, ".env"))
|
||||||
|
}
|
||||||
|
for _, dotEnvFile := range dotEnvFiles {
|
||||||
|
abs, err := filepath.Abs(dotEnvFile)
|
||||||
|
if err != nil {
|
||||||
|
return envMap, err
|
||||||
|
}
|
||||||
|
dotEnvFile = abs
|
||||||
|
|
||||||
|
s, err := os.Stat(dotEnvFile)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
if len(filenames) == 0 {
|
||||||
|
return envMap, nil
|
||||||
|
}
|
||||||
|
return envMap, errors.Errorf("Couldn't find env file: %s", dotEnvFile)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return envMap, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.IsDir() {
|
||||||
|
if len(filenames) == 0 {
|
||||||
|
return envMap, nil
|
||||||
|
}
|
||||||
|
return envMap, errors.Errorf("%s is a directory", dotEnvFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := os.ReadFile(dotEnvFile)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, errors.Errorf("Couldn't read env file: %s", dotEnvFile)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return envMap, err
|
||||||
|
}
|
||||||
|
|
||||||
|
env, err := ParseWithLookup(bytes.NewReader(b), func(k string) (string, bool) {
|
||||||
|
v, ok := currentEnv[k]
|
||||||
|
if ok {
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
v, ok = envMap[k]
|
||||||
|
return v, ok
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return envMap, errors.Wrapf(err, "failed to read %s", dotEnvFile)
|
||||||
|
}
|
||||||
|
for k, v := range env {
|
||||||
|
envMap[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return envMap, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Compose Specification Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/dotenv"
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadIncludeConfig parse the require config from raw yaml
|
||||||
|
func LoadIncludeConfig(source []interface{}) ([]types.IncludeConfig, error) {
|
||||||
|
var requires []types.IncludeConfig
|
||||||
|
err := Transform(source, &requires)
|
||||||
|
return requires, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var transformIncludeConfig TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||||
|
switch value := data.(type) {
|
||||||
|
case string:
|
||||||
|
return map[string]interface{}{"path": value}, nil
|
||||||
|
case map[string]interface{}:
|
||||||
|
return value, nil
|
||||||
|
default:
|
||||||
|
return data, errors.Errorf("invalid type %T for `include` configuration", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadInclude(configDetails types.ConfigDetails, model *types.Config, options *Options, loaded []string) (*types.Config, error) {
|
||||||
|
for _, r := range model.Include {
|
||||||
|
for i, p := range r.Path {
|
||||||
|
if !filepath.IsAbs(p) {
|
||||||
|
r.Path[i] = filepath.Join(configDetails.WorkingDir, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.ProjectDirectory == "" {
|
||||||
|
r.ProjectDirectory = filepath.Dir(r.Path[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
loadOptions := options.clone()
|
||||||
|
loadOptions.SetProjectName(model.Name, true)
|
||||||
|
loadOptions.ResolvePaths = true
|
||||||
|
loadOptions.SkipNormalization = true
|
||||||
|
loadOptions.SkipConsistencyCheck = true
|
||||||
|
|
||||||
|
env, err := dotenv.GetEnvFromFile(configDetails.Environment, r.ProjectDirectory, r.EnvFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
imported, err := load(types.ConfigDetails{
|
||||||
|
WorkingDir: r.ProjectDirectory,
|
||||||
|
ConfigFiles: types.ToConfigFiles(r.Path),
|
||||||
|
Environment: env,
|
||||||
|
}, loadOptions, loaded)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = importResources(model, imported, r.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
model.Include = nil
|
||||||
|
return model, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// importResources import into model all resources defined by imported, and report error on conflict
|
||||||
|
func importResources(model *types.Config, imported *types.Project, path []string) error {
|
||||||
|
services := mapByName(model.Services)
|
||||||
|
for _, service := range imported.Services {
|
||||||
|
if _, ok := services[service.Name]; ok {
|
||||||
|
return fmt.Errorf("imported compose file %s defines conflicting service %s", path, service.Name)
|
||||||
|
}
|
||||||
|
model.Services = append(model.Services, service)
|
||||||
|
}
|
||||||
|
for n, network := range imported.Networks {
|
||||||
|
if _, ok := model.Networks[n]; ok {
|
||||||
|
return fmt.Errorf("imported compose file %s defines conflicting network %s", path, n)
|
||||||
|
}
|
||||||
|
model.Networks[n] = network
|
||||||
|
}
|
||||||
|
for n, volume := range imported.Volumes {
|
||||||
|
if _, ok := model.Volumes[n]; ok {
|
||||||
|
return fmt.Errorf("imported compose file %s defines conflicting volume %s", path, n)
|
||||||
|
}
|
||||||
|
model.Volumes[n] = volume
|
||||||
|
}
|
||||||
|
for n, secret := range imported.Secrets {
|
||||||
|
if _, ok := model.Secrets[n]; ok {
|
||||||
|
return fmt.Errorf("imported compose file %s defines conflicting secret %s", path, n)
|
||||||
|
}
|
||||||
|
model.Secrets[n] = secret
|
||||||
|
}
|
||||||
|
for n, config := range imported.Configs {
|
||||||
|
if _, ok := model.Configs[n]; ok {
|
||||||
|
return fmt.Errorf("imported compose file %s defines conflicting config %s", path, n)
|
||||||
|
}
|
||||||
|
model.Configs[n] = config
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Compose Specification Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResolveRelativePaths resolves relative paths based on project WorkingDirectory
|
||||||
|
func ResolveRelativePaths(project *types.Project) error {
|
||||||
|
absWorkingDir, err := filepath.Abs(project.WorkingDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
project.WorkingDir = absWorkingDir
|
||||||
|
|
||||||
|
absComposeFiles, err := absComposeFiles(project.ComposeFiles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
project.ComposeFiles = absComposeFiles
|
||||||
|
|
||||||
|
for i, s := range project.Services {
|
||||||
|
ResolveServiceRelativePaths(project.WorkingDir, &s)
|
||||||
|
project.Services[i] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, obj := range project.Configs {
|
||||||
|
if obj.File != "" {
|
||||||
|
obj.File = absPath(project.WorkingDir, obj.File)
|
||||||
|
project.Configs[i] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, obj := range project.Secrets {
|
||||||
|
if obj.File != "" {
|
||||||
|
obj.File = resolveMaybeUnixPath(project.WorkingDir, obj.File)
|
||||||
|
project.Secrets[i] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, config := range project.Volumes {
|
||||||
|
if config.Driver == "local" && config.DriverOpts["o"] == "bind" {
|
||||||
|
// This is actually a bind mount
|
||||||
|
config.DriverOpts["device"] = resolveMaybeUnixPath(project.WorkingDir, config.DriverOpts["device"])
|
||||||
|
project.Volumes[name] = config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveServiceRelativePaths(workingDir string, s *types.ServiceConfig) {
|
||||||
|
if s.Build != nil {
|
||||||
|
if !isRemoteContext(s.Build.Context) {
|
||||||
|
s.Build.Context = absPath(workingDir, s.Build.Context)
|
||||||
|
}
|
||||||
|
for name, path := range s.Build.AdditionalContexts {
|
||||||
|
if strings.Contains(path, "://") { // `docker-image://` or any builder specific context type
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if isRemoteContext(path) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.Build.AdditionalContexts[name] = absPath(workingDir, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for j, f := range s.EnvFile {
|
||||||
|
s.EnvFile[j] = absPath(workingDir, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Extends != nil && s.Extends.File != "" {
|
||||||
|
s.Extends.File = absPath(workingDir, s.Extends.File)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, vol := range s.Volumes {
|
||||||
|
if vol.Type != types.VolumeTypeBind {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.Volumes[i].Source = resolveMaybeUnixPath(workingDir, vol.Source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func absPath(workingDir string, filePath string) string {
|
||||||
|
if strings.HasPrefix(filePath, "~") {
|
||||||
|
home, _ := os.UserHomeDir()
|
||||||
|
return filepath.Join(home, filePath[1:])
|
||||||
|
}
|
||||||
|
if filepath.IsAbs(filePath) {
|
||||||
|
return filePath
|
||||||
|
}
|
||||||
|
return filepath.Join(workingDir, filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func absComposeFiles(composeFiles []string) ([]string, error) {
|
||||||
|
for i, composeFile := range composeFiles {
|
||||||
|
absComposefile, err := filepath.Abs(composeFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
composeFiles[i] = absComposefile
|
||||||
|
}
|
||||||
|
return composeFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRemoteContext returns true if the value is a Git reference or HTTP(S) URL.
|
||||||
|
//
|
||||||
|
// Any other value is assumed to be a local filesystem path and returns false.
|
||||||
|
//
|
||||||
|
// See: https://github.com/moby/buildkit/blob/18fc875d9bfd6e065cd8211abc639434ba65aa56/frontend/dockerui/context.go#L76-L79
|
||||||
|
func isRemoteContext(maybeURL string) bool {
|
||||||
|
for _, prefix := range []string{"https://", "http://", "git://", "ssh://", "github.com/", "git@"} {
|
||||||
|
if strings.HasPrefix(maybeURL, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Compose Specification Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
func MapKeys[T comparable, U any](theMap map[T]U) []T {
|
||||||
|
var result []T
|
||||||
|
for key := range theMap {
|
||||||
|
result = append(result, key)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapsAppend[T comparable, U any](target map[T]U, source map[T]U) map[T]U {
|
||||||
|
if target == nil {
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
if source == nil {
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
for key, value := range source {
|
||||||
|
if _, ok := target[key]; !ok {
|
||||||
|
target[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
func ArrayContains[T comparable](source []T, toCheck []T) bool {
|
||||||
|
for _, value := range toCheck {
|
||||||
|
if !slices.Contains(source, value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
[568].out
|
||||||
|
_go*
|
||||||
|
_test*
|
||||||
|
_obj
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
ARG GOVERSION=1.14
|
||||||
|
FROM golang:${GOVERSION}
|
||||||
|
|
||||||
|
# Set base env.
|
||||||
|
ARG GOOS=linux
|
||||||
|
ARG GOARCH=amd64
|
||||||
|
ENV GOOS=${GOOS} GOARCH=${GOARCH} CGO_ENABLED=0 GOFLAGS='-v -ldflags=-s -ldflags=-w'
|
||||||
|
|
||||||
|
# Pre compile the stdlib for 386/arm (32bits).
|
||||||
|
RUN go build -a std
|
||||||
|
|
||||||
|
# Add the code to the image.
|
||||||
|
WORKDIR pty
|
||||||
|
ADD . .
|
||||||
|
|
||||||
|
# Build the lib.
|
||||||
|
RUN go build
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
# NOTE: Using 1.13 as a base to build the RISCV compiler, the resulting version is based on go1.6.
|
||||||
|
FROM golang:1.13
|
||||||
|
|
||||||
|
# Clone and complie a riscv compatible version of the go compiler.
|
||||||
|
RUN git clone https://review.gerrithub.io/riscv/riscv-go /riscv-go
|
||||||
|
# riscvdev branch HEAD as of 2019-06-29.
|
||||||
|
RUN cd /riscv-go && git checkout 04885fddd096d09d4450726064d06dd107e374bf
|
||||||
|
ENV PATH=/riscv-go/misc/riscv:/riscv-go/bin:$PATH
|
||||||
|
RUN cd /riscv-go/src && GOROOT_BOOTSTRAP=$(go env GOROOT) ./make.bash
|
||||||
|
ENV GOROOT=/riscv-go
|
||||||
|
|
||||||
|
# Set the base env.
|
||||||
|
ENV GOOS=linux GOARCH=riscv CGO_ENABLED=0 GOFLAGS='-v -ldflags=-s -ldflags=-w'
|
||||||
|
|
||||||
|
# Pre compile the stdlib.
|
||||||
|
RUN go build -a std
|
||||||
|
|
||||||
|
# Add the code to the image.
|
||||||
|
WORKDIR pty
|
||||||
|
ADD . .
|
||||||
|
|
||||||
|
# Build the lib.
|
||||||
|
RUN go build
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
Copyright (c) 2011 Keith Rarick
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute,
|
||||||
|
sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall
|
||||||
|
be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||||
|
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
# pty
|
||||||
|
|
||||||
|
Pty is a Go package for using unix pseudo-terminals.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get github.com/creack/pty
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Note that those examples are for demonstration purpose only, to showcase how to use the library. They are not meant to be used in any kind of production environment.
|
||||||
|
|
||||||
|
### Command
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/creack/pty"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c := exec.Command("grep", "--color=auto", "bar")
|
||||||
|
f, err := pty.Start(c)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
f.Write([]byte("foo\n"))
|
||||||
|
f.Write([]byte("bar\n"))
|
||||||
|
f.Write([]byte("baz\n"))
|
||||||
|
f.Write([]byte{4}) // EOT
|
||||||
|
}()
|
||||||
|
io.Copy(os.Stdout, f)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shell
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/creack/pty"
|
||||||
|
"golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
func test() error {
|
||||||
|
// Create arbitrary command.
|
||||||
|
c := exec.Command("bash")
|
||||||
|
|
||||||
|
// Start the command with a pty.
|
||||||
|
ptmx, err := pty.Start(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Make sure to close the pty at the end.
|
||||||
|
defer func() { _ = ptmx.Close() }() // Best effort.
|
||||||
|
|
||||||
|
// Handle pty size.
|
||||||
|
ch := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(ch, syscall.SIGWINCH)
|
||||||
|
go func() {
|
||||||
|
for range ch {
|
||||||
|
if err := pty.InheritSize(os.Stdin, ptmx); err != nil {
|
||||||
|
log.Printf("error resizing pty: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ch <- syscall.SIGWINCH // Initial resize.
|
||||||
|
defer func() { signal.Stop(ch); close(ch) }() // Cleanup signals when done.
|
||||||
|
|
||||||
|
// Set stdin in raw mode.
|
||||||
|
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer func() { _ = term.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
|
||||||
|
|
||||||
|
// Copy stdin to the pty and the pty to stdout.
|
||||||
|
// NOTE: The goroutine will keep reading until the next keystroke before returning.
|
||||||
|
go func() { _, _ = io.Copy(ptmx, os.Stdin) }()
|
||||||
|
_, _ = io.Copy(os.Stdout, ptmx)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := test(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build gc
|
||||||
|
//+build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for amd64, Solaris are implemented in runtime/syscall_solaris.go
|
||||||
|
//
|
||||||
|
|
||||||
|
TEXT ·sysvicall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·sysvicall6(SB)
|
||||||
|
|
||||||
|
TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·rawSysvicall6(SB)
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
// Package pty provides functions for working with Unix terminals.
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrUnsupported is returned if a function is not
|
||||||
|
// available on the current platform.
|
||||||
|
var ErrUnsupported = errors.New("unsupported")
|
||||||
|
|
||||||
|
// Open a pty and its corresponding tty.
|
||||||
|
func Open() (pty, tty *os.File, err error) {
|
||||||
|
return open()
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
//go:build !windows && !solaris && !aix
|
||||||
|
// +build !windows,!solaris,!aix
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
const (
|
||||||
|
TIOCGWINSZ = syscall.TIOCGWINSZ
|
||||||
|
TIOCSWINSZ = syscall.TIOCSWINSZ
|
||||||
|
)
|
||||||
|
|
||||||
|
func ioctl(fd, cmd, ptr uintptr) error {
|
||||||
|
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
|
||||||
|
if e != 0 {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
|
||||||
|
// +build darwin dragonfly freebsd netbsd openbsd
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
// from <sys/ioccom.h>
|
||||||
|
const (
|
||||||
|
_IOC_VOID uintptr = 0x20000000
|
||||||
|
_IOC_OUT uintptr = 0x40000000
|
||||||
|
_IOC_IN uintptr = 0x80000000
|
||||||
|
_IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN
|
||||||
|
_IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN
|
||||||
|
|
||||||
|
_IOC_PARAM_SHIFT = 13
|
||||||
|
_IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func _IOC_PARM_LEN(ioctl uintptr) uintptr {
|
||||||
|
return (ioctl >> 16) & _IOC_PARAM_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IO(group byte, ioctl_num uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_VOID, group, ioctl_num, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_OUT, group, ioctl_num, param_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_IN, group, ioctl_num, param_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len)
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
//go:build solaris
|
||||||
|
// +build solaris
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:cgo_import_dynamic libc_ioctl ioctl "libc.so"
|
||||||
|
//go:linkname procioctl libc_ioctl
|
||||||
|
var procioctl uintptr
|
||||||
|
|
||||||
|
const (
|
||||||
|
// see /usr/include/sys/stropts.h
|
||||||
|
I_PUSH = uintptr((int32('S')<<8 | 002))
|
||||||
|
I_STR = uintptr((int32('S')<<8 | 010))
|
||||||
|
I_FIND = uintptr((int32('S')<<8 | 013))
|
||||||
|
|
||||||
|
// see /usr/include/sys/ptms.h
|
||||||
|
ISPTM = (int32('P') << 8) | 1
|
||||||
|
UNLKPT = (int32('P') << 8) | 2
|
||||||
|
PTSSTTY = (int32('P') << 8) | 3
|
||||||
|
ZONEPT = (int32('P') << 8) | 4
|
||||||
|
OWNERPT = (int32('P') << 8) | 5
|
||||||
|
|
||||||
|
// see /usr/include/sys/termios.h
|
||||||
|
TIOCSWINSZ = (uint32('T') << 8) | 103
|
||||||
|
TIOCGWINSZ = (uint32('T') << 8) | 104
|
||||||
|
)
|
||||||
|
|
||||||
|
type strioctl struct {
|
||||||
|
icCmd int32
|
||||||
|
icTimeout int32
|
||||||
|
icLen int32
|
||||||
|
icDP unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defined in asm_solaris_amd64.s.
|
||||||
|
func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||||
|
|
||||||
|
func ioctl(fd, cmd, ptr uintptr) error {
|
||||||
|
if _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procioctl)), 3, fd, cmd, ptr, 0, 0, 0); errno != 0 {
|
||||||
|
return errno
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
//go:build aix
|
||||||
|
// +build aix
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
const (
|
||||||
|
TIOCGWINSZ = 0
|
||||||
|
TIOCSWINSZ = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
func ioctl(fd, cmd, ptr uintptr) error {
|
||||||
|
return ErrUnsupported
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
GOOSARCH="${GOOS}_${GOARCH}"
|
||||||
|
case "$GOOSARCH" in
|
||||||
|
_* | *_ | _)
|
||||||
|
echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
GODEFS="go tool cgo -godefs"
|
||||||
|
|
||||||
|
$GODEFS types.go |gofmt > ztypes_$GOARCH.go
|
||||||
|
|
||||||
|
case $GOOS in
|
||||||
|
freebsd|dragonfly|netbsd|openbsd)
|
||||||
|
$GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
//go:build darwin
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
p := os.NewFile(uintptr(pFD), "/dev/ptmx")
|
||||||
|
// In case of error after this point, make sure we close the ptmx fd.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = p.Close() // Best effort.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sname, err := ptsname(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := grantpt(p); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := unlockpt(p); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return p, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
|
||||||
|
|
||||||
|
err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range n {
|
||||||
|
if c == 0 {
|
||||||
|
return string(n[:i]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
|
||||||
|
}
|
||||||
|
|
||||||
|
func grantpt(f *os.File) error {
|
||||||
|
return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockpt(f *os.File) error {
|
||||||
|
return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
//go:build dragonfly
|
||||||
|
// +build dragonfly
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// same code as pty_darwin.go
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// In case of error after this point, make sure we close the ptmx fd.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = p.Close() // Best effort.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sname, err := ptsname(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := grantpt(p); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := unlockpt(p); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.OpenFile(sname, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return p, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func grantpt(f *os.File) error {
|
||||||
|
_, err := isptmaster(f.Fd())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockpt(f *os.File) error {
|
||||||
|
_, err := isptmaster(f.Fd())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func isptmaster(fd uintptr) (bool, error) {
|
||||||
|
err := ioctl(fd, syscall.TIOCISPTMASTER, 0)
|
||||||
|
return err == nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
emptyFiodgnameArg fiodgnameArg
|
||||||
|
ioctl_FIODNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
|
||||||
|
)
|
||||||
|
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
name := make([]byte, _C_SPECNAMELEN)
|
||||||
|
fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}}
|
||||||
|
|
||||||
|
err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range name {
|
||||||
|
if c == 0 {
|
||||||
|
s := "/dev/" + string(name[:i])
|
||||||
|
return strings.Replace(s, "ptm", "pts", -1), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
//go:build freebsd
|
||||||
|
// +build freebsd
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func posixOpenpt(oflag int) (fd int, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
|
||||||
|
fd = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return fd, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
fd, err := posixOpenpt(syscall.O_RDWR | syscall.O_CLOEXEC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
p := os.NewFile(uintptr(fd), "/dev/pts")
|
||||||
|
// In case of error after this point, make sure we close the pts fd.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = p.Close() // Best effort.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sname, err := ptsname(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return p, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isptmaster(fd uintptr) (bool, error) {
|
||||||
|
err := ioctl(fd, syscall.TIOCPTMASTER, 0)
|
||||||
|
return err == nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
emptyFiodgnameArg fiodgnameArg
|
||||||
|
ioctlFIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
|
||||||
|
)
|
||||||
|
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
master, err := isptmaster(f.Fd())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !master {
|
||||||
|
return "", syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = _C_SPECNAMELEN + 1
|
||||||
|
var (
|
||||||
|
buf = make([]byte, n)
|
||||||
|
arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
|
||||||
|
)
|
||||||
|
if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range buf {
|
||||||
|
if c == 0 {
|
||||||
|
return string(buf[:i]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("FIODGNAME string not NUL-terminated")
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue