Merge pull request #1155 from docker/remove_example_backend

This commit is contained in:
Nicolas De loof 2021-01-19 11:55:19 +01:00 committed by GitHub
commit ed67111b3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 83 additions and 360 deletions

View File

@ -65,12 +65,11 @@ jobs:
- name: Test
env:
BUILD_TAGS: example
run: make -f builder.Makefile test
- name: Build for local E2E
env:
BUILD_TAGS: example,e2e
BUILD_TAGS: e2e
run: make -f builder.Makefile cli
- name: E2E Test

View File

@ -48,13 +48,11 @@ jobs:
key: go-${{ hashFiles('**/go.sum') }}
- name: Test
env:
BUILD_TAGS: example,local
run: make -f builder.Makefile test
- name: Build
env:
BUILD_TAGS: example,local,e2e
BUILD_TAGS: e2e
run: make -f builder.Makefile cli
- name: E2E Test

View File

@ -35,22 +35,6 @@ This will create a symbolic link from the existing Docker CLI to
You can statically cross compile the CLI for Windows, macOS, and Linux using the
`cross` target.
### Building with specific backends
You can specify which backends are build using the `BUILD_TAGS` variable.
The available backends are:
* `aci`: For ACI support (always built)
* `ecs`: For ECS support (always built)
* `example`: Testing backend (off by default)
* `local`: Beginnings of a [moby](https://github.com/moby/moby) backend
(off by default)
If you want the ACI, ECS and example backends, then you can build as follows:
```console
make BUILD_TAGS=example cli
```
### Updating the API code
The API provided by the CLI is defined using protobuf. If you make changes to

View File

@ -39,7 +39,7 @@ protos: ## Generate go code from .proto files
cli: ## Compile the cli
@docker build . --target cli \
--platform local \
--build-arg BUILD_TAGS=example,e2e \
--build-arg BUILD_TAGS=e2e \
--build-arg GIT_TAG=$(GIT_TAG) \
--output ./bin
@ -63,7 +63,6 @@ cross: ## Compile the CLI for linux, darwin and windows
test: ## Run unit tests
@docker build . \
--build-arg BUILD_TAGS=example \
--build-arg GIT_TAG=$(GIT_TAG) \
--target test
@ -72,7 +71,7 @@ cache-clear: ## Clear the builder cache
lint: ## run linter(s)
@docker build . \
--build-arg BUILD_TAGS=example,e2e \
--build-arg BUILD_TAGS=e2e \
--build-arg GIT_TAG=$(GIT_TAG) \
--target lint

View File

@ -61,9 +61,6 @@ type AwsContext EcsContext
// LocalContext is the context for the local backend
type LocalContext struct{}
// ExampleContext is the context for the example backend
type ExampleContext struct{}
// MarshalJSON implements custom JSON marshalling
func (dc ContextMetadata) MarshalJSON() ([]byte, error) {
s := map[string]interface{}{}

View File

@ -55,9 +55,6 @@ const (
// LocalContextType is the endpoint key in the context endpoints for a new
// local backend
LocalContextType = "local"
// ExampleContextType is the endpoint key in the context endpoints for an
// example backend
ExampleContextType = "example"
)
const (
@ -331,8 +328,5 @@ func getters() map[string]func() interface{} {
LocalContextType: func() interface{} {
return &LocalContext{}
},
ExampleContextType: func() interface{} {
return &ExampleContext{}
},
}
}

View File

@ -64,8 +64,8 @@ func TestGetEndpoint(t *testing.T) {
assert.NilError(t, err)
assert.Equal(t, ctx.Location, "eu")
var exampleCtx ExampleContext
err = s.GetEndpoint("aci", &exampleCtx)
var localCtx LocalContext
err = s.GetEndpoint("aci", &localCtx)
assert.Error(t, err, "wrong context type")
}

View File

@ -39,7 +39,7 @@ func createCommand() *cobra.Command {
longHelp := fmt.Sprintf(`Create a new context
Create docker engine context:
Create docker engine context:
$ docker context create CONTEXT [flags]
%s
@ -78,7 +78,6 @@ $ docker context create my-context --description "some description" --docker "ho
cmd.AddCommand(
createLocalCommand(),
createExampleCommand(),
)
for _, command := range extraCommands {
cmd.AddCommand(command())
@ -111,22 +110,6 @@ func createLocalCommand() *cobra.Command {
return cmd
}
func createExampleCommand() *cobra.Command {
var opts descriptionCreateOpts
cmd := &cobra.Command{
Use: "example CONTEXT",
Short: "Create a test context returning fixed output",
Args: cobra.ExactArgs(1),
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
return createDockerContext(cmd.Context(), args[0], store.ExampleContextType, opts.description, store.ExampleContext{})
},
}
addDescriptionFlag(cmd, &opts.description)
return cmd
}
func createDockerContext(ctx context.Context, name string, contextType string, description string, data interface{}) error {
s := store.ContextStore(ctx)
result := s.Create(

View File

@ -51,7 +51,6 @@ import (
_ "github.com/docker/compose-cli/aci"
_ "github.com/docker/compose-cli/ecs"
_ "github.com/docker/compose-cli/ecs/local"
_ "github.com/docker/compose-cli/example"
_ "github.com/docker/compose-cli/local"
)

View File

@ -20,7 +20,6 @@ These constraints resulted in the following architecture:
What follows is a list of useful links to help navigate the code:
* The CLI UX code is in [`cli/`](../cli)
* The backend interface is defined in [`backend/`](../backend)
* An example backend can be found in [`example/`](../example)
* The API is defined by protobufs that can be found in [`protos/`](../protos)
* The API server is in [`server/`](../server)
* The context management and interface can be found in [`context/`](../api/context)

View File

@ -1,187 +0,0 @@
// +build example
/*
Copyright 2020 Docker Compose CLI 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 example
import (
"context"
"errors"
"fmt"
"github.com/docker/compose-cli/api/backend"
"github.com/docker/compose-cli/api/cloud"
"github.com/docker/compose-cli/api/compose"
"github.com/docker/compose-cli/api/containers"
"github.com/docker/compose-cli/api/errdefs"
"github.com/docker/compose-cli/api/resources"
"github.com/docker/compose-cli/api/secrets"
"github.com/docker/compose-cli/api/volumes"
"github.com/compose-spec/compose-go/types"
)
type apiService struct {
containerService
composeService
}
func (a *apiService) ContainerService() containers.Service {
return &a.containerService
}
func (a *apiService) ComposeService() compose.Service {
return &a.composeService
}
func (a *apiService) SecretsService() secrets.Service {
return nil
}
func (a *apiService) VolumeService() volumes.Service {
return nil
}
func (a *apiService) ResourceService() resources.Service {
return nil
}
func init() {
backend.Register("example", "example", service, cloud.NotImplementedCloudService)
}
func service(ctx context.Context) (backend.Service, error) {
return &apiService{}, nil
}
type containerService struct{}
func (cs *containerService) Inspect(ctx context.Context, id string) (containers.Container, error) {
return containers.Container{
ID: "id",
Image: "nginx",
Platform: "Linux",
HostConfig: &containers.HostConfig{
RestartPolicy: "none",
},
}, nil
}
func (cs *containerService) List(ctx context.Context, all bool) ([]containers.Container, error) {
result := []containers.Container{
{
ID: "id",
Image: "nginx",
},
{
ID: "1234",
Image: "alpine",
},
}
if all {
result = append(result, containers.Container{
ID: "stopped",
Image: "nginx",
})
}
return result, nil
}
func (cs *containerService) Run(ctx context.Context, r containers.ContainerConfig) error {
fmt.Printf("Running container %q with name %q\n", r.Image, r.ID)
return nil
}
func (cs *containerService) Start(ctx context.Context, containerID string) error {
return errors.New("not implemented")
}
func (cs *containerService) Stop(ctx context.Context, containerName string, timeout *uint32) error {
return errors.New("not implemented")
}
func (cs *containerService) Kill(ctx context.Context, containerName string, signal string) error {
return errors.New("not implemented")
}
func (cs *containerService) Exec(ctx context.Context, name string, request containers.ExecRequest) error {
fmt.Printf("Executing command %q on container %q", request.Command, name)
return nil
}
func (cs *containerService) Logs(ctx context.Context, containerName string, request containers.LogsRequest) error {
fmt.Fprintf(request.Writer, "Following logs for container %q", containerName)
return nil
}
func (cs *containerService) Delete(ctx context.Context, id string, request containers.DeleteRequest) error {
fmt.Printf("Deleting container %q with force = %t\n", id, request.Force)
return nil
}
type composeService struct{}
func (cs *composeService) Build(ctx context.Context, project *types.Project) error {
fmt.Printf("Build command on project %q", project.Name)
return nil
}
func (cs *composeService) Push(ctx context.Context, project *types.Project) error {
return errdefs.ErrNotImplemented
}
func (cs *composeService) Pull(ctx context.Context, project *types.Project) error {
return errdefs.ErrNotImplemented
}
func (cs *composeService) Create(ctx context.Context, project *types.Project, opts compose.CreateOptions) error {
return errdefs.ErrNotImplemented
}
func (cs *composeService) Start(ctx context.Context, project *types.Project, consumer compose.LogConsumer) error {
return errdefs.ErrNotImplemented
}
func (cs *composeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
fmt.Printf("Up command on project %q", project.Name)
return nil
}
func (cs *composeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
fmt.Printf("Down command on project %q", projectName)
return nil
}
func (cs *composeService) Ps(ctx context.Context, projectName string) ([]compose.ContainerSummary, error) {
return nil, errdefs.ErrNotImplemented
}
func (cs *composeService) List(ctx context.Context, project string) ([]compose.Stack, error) {
return nil, errdefs.ErrNotImplemented
}
func (cs *composeService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer, options compose.LogOptions) error {
return errdefs.ErrNotImplemented
}
func (cs *composeService) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
return nil, errdefs.ErrNotImplemented
}
func (cs *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
return errdefs.ErrNotImplemented
}

View File

@ -1,17 +0,0 @@
/*
Copyright 2020 Docker Compose CLI 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 example

View File

@ -3,23 +3,14 @@
forbiddenImports:
- github.com/docker/compose-cli/cli
- github.com/docker/compose-cli/ecs
- github.com/docker/compose-cli/example
- github.com/docker/compose-cli/local
- path: ./ecs
forbiddenImports:
- github.com/docker/compose-cli/aci
- github.com/docker/compose-cli/cli
- github.com/docker/compose-cli/example
- github.com/docker/compose-cli/local
- path: ./example
forbiddenImports:
- github.com/docker/compose-cli/aci
- github.com/docker/compose-cli/cli
- github.com/docker/compose-cli/ecs
- github.com/docker/compose-cli/local
- path: ./local
forbiddenImports:
- github.com/docker/compose-cli/aci
- github.com/docker/compose-cli/cli
- github.com/docker/compose-cli/ecs
- github.com/docker/compose-cli/example

View File

@ -189,23 +189,25 @@ func TestContextMetrics(t *testing.T) {
t.Run("metrics on other context type", func(t *testing.T) {
s.ResetUsage()
c.RunDockerCmd("context", "create", "example", "test-example")
c.RunDockerCmd("context", "create", "local", "test-local")
c.RunDockerCmd("ps")
c.RunDockerCmd("context", "use", "test-example")
c.RunDockerCmd("context", "use", "test-local")
c.RunDockerCmd("ps")
c.RunDockerOrExitError("stop", "unknown")
c.RunDockerCmd("context", "use", "default")
c.RunDockerCmd("--context", "test-example", "ps")
c.RunDockerCmd("--context", "test-local", "ps")
c.RunDockerCmd("context", "ls")
usage := s.GetUsage()
assert.DeepEqual(t, []string{
`{"command":"context create","context":"moby","source":"cli","status":"success"}`,
`{"command":"ps","context":"moby","source":"cli","status":"success"}`,
`{"command":"context use","context":"moby","source":"cli","status":"success"}`,
`{"command":"ps","context":"example","source":"cli","status":"success"}`,
`{"command":"stop","context":"example","source":"cli","status":"failure"}`,
`{"command":"context use","context":"example","source":"cli","status":"success"}`,
`{"command":"ps","context":"example","source":"cli","status":"success"}`,
`{"command":"ps","context":"local","source":"cli","status":"success"}`,
`{"command":"stop","context":"local","source":"cli","status":"failure"}`,
`{"command":"context use","context":"local","source":"cli","status":"success"}`,
`{"command":"ps","context":"local","source":"cli","status":"success"}`,
`{"command":"context ls","context":"moby","source":"cli","status":"success"}`,
}, usage)
})
}
@ -418,16 +420,15 @@ func TestLegacy(t *testing.T) {
})
t.Run("host flag overrides context", func(t *testing.T) {
c.RunDockerCmd("context", "create", "example", "test-example")
c.RunDockerCmd("context", "use", "test-example")
c.RunDockerCmd("context", "create", "local", "test-local")
c.RunDockerCmd("context", "use", "test-local")
endpoint := "unix:///var/run/docker.sock"
if runtime.GOOS == "windows" {
endpoint = "npipe:////./pipe/docker_engine"
}
res := c.RunDockerCmd("-H", endpoint, "ps")
// Example backend's ps output includes these strings
assert.Assert(t, !strings.Contains(res.Stdout(), "id"), "%q does not contains %q", res.Stdout(), "id")
assert.Assert(t, !strings.Contains(res.Stdout(), "1234"), "%q does not contains %q", res.Stdout(), "1234")
res := c.RunDockerCmd("-H", endpoint, "images")
// Local backend does not have images command
assert.Assert(t, strings.Contains(res.Stdout(), "IMAGE ID"), res.Stdout())
})
}
@ -459,11 +460,11 @@ func TestLegacyLogin(t *testing.T) {
func TestUnsupportedCommand(t *testing.T) {
c := NewParallelE2eCLI(t, binDir)
c.RunDockerCmd("context", "create", "example", "test-example")
res := c.RunDockerOrExitError("--context", "test-example", "images")
c.RunDockerCmd("context", "create", "local", "test-local")
res := c.RunDockerOrExitError("--context", "test-local", "images")
res.Assert(t, icmd.Expected{
ExitCode: 1,
Err: `Command "images" not available in current context (test-example), you can use the "default" context to run this command`,
Err: `Command "images" not available in current context (test-local), you can use the "default" context to run this command`,
})
}
@ -519,60 +520,13 @@ func TestVersion(t *testing.T) {
})
t.Run("delegate version flag", func(t *testing.T) {
c.RunDockerCmd("context", "create", "example", "test-example")
c.RunDockerCmd("context", "use", "test-example")
c.RunDockerCmd("context", "create", "local", "test-local")
c.RunDockerCmd("context", "use", "test-local")
res := c.RunDockerCmd("-v")
res.Assert(t, icmd.Expected{Out: "Docker version"})
})
}
func TestMockBackend(t *testing.T) {
c := NewParallelE2eCLI(t, binDir)
c.RunDockerCmd("context", "create", "example", "test-example")
res := c.RunDockerCmd("context", "use", "test-example")
res.Assert(t, icmd.Expected{Out: "test-example"})
t.Run("use", func(t *testing.T) {
res := c.RunDockerCmd("context", "show")
res.Assert(t, icmd.Expected{Out: "test-example"})
res = c.RunDockerCmd("context", "ls")
golden.Assert(t, res.Stdout(), GoldenFile("ls-out-test-example"))
})
t.Run("ps", func(t *testing.T) {
res := c.RunDockerCmd("ps")
golden.Assert(t, res.Stdout(), "ps-out-example.golden")
res = c.RunDockerCmd("ps", "--format", "pretty")
golden.Assert(t, res.Stdout(), "ps-out-example.golden")
res = c.RunDockerCmd("ps", "--format", "json")
golden.Assert(t, res.Stdout(), "ps-out-example-json.golden")
})
t.Run("ps quiet", func(t *testing.T) {
res := c.RunDockerCmd("ps", "-q")
golden.Assert(t, res.Stdout(), "ps-quiet-out-example.golden")
})
t.Run("ps quiet all", func(t *testing.T) {
res := c.RunDockerCmd("ps", "-q", "--all")
golden.Assert(t, res.Stdout(), "ps-quiet-all-out-example.golden")
})
t.Run("inspect", func(t *testing.T) {
res := c.RunDockerCmd("inspect", "id")
golden.Assert(t, res.Stdout(), "inspect-id.golden")
})
t.Run("run", func(t *testing.T) {
res := c.RunDockerCmd("run", "-d", "nginx", "-p", "80:80")
res.Assert(t, icmd.Expected{
Out: `Running container "nginx" with name`,
})
})
}
func TestFailOnEcsUsageAsPlugin(t *testing.T) {
c := NewParallelE2eCLI(t, binDir)
res := c.RunDockerCmd("context", "create", "local", "local")

View File

@ -1,14 +0,0 @@
{
"ID": "id",
"Status": "",
"Image": "nginx",
"HostConfig": {
"RestartPolicy": "none",
"CPUReservation": 0,
"CPULimit": 0,
"MemoryReservation": 0,
"MemoryLimit": 0,
"AutoRemove": false
},
"Platform": "Linux"
}

View File

@ -1,3 +0,0 @@
NAME TYPE DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
default moby Current DOCKER_HOST based configuration npipe:////./pipe/docker_engine swarm
test-example * example

View File

@ -1 +0,0 @@
[{"ID":"id","Image":"nginx","Status":"","Command":"","Ports":[]},{"ID":"1234","Image":"alpine","Status":"","Command":"","Ports":[]}]

View File

@ -1,3 +0,0 @@
CONTAINER ID IMAGE COMMAND STATUS PORTS
id nginx
1234 alpine

View File

@ -1,3 +0,0 @@
id
1234
stopped

View File

@ -1,2 +0,0 @@
id
1234

View File

@ -17,6 +17,7 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
@ -27,10 +28,12 @@ import (
"testing"
"time"
"gotest.tools/golden"
"gotest.tools/v3/assert"
"gotest.tools/v3/icmd"
"gotest.tools/v3/poll"
"github.com/docker/compose-cli/cli/cmd"
. "github.com/docker/compose-cli/tests/framework"
)
@ -92,6 +95,59 @@ func TestKillChildProcess(t *testing.T) {
poll.WaitOn(t, buildStopped, poll.WithDelay(1*time.Second), poll.WithTimeout(60*time.Second))
}
// no linux containers on GHA Windows CI nodes (windows server)
func TestLocalContainers(t *testing.T) {
c := NewParallelE2eCLI(t, binDir)
c.RunDockerCmd("context", "create", "local", "test-local")
res := c.RunDockerCmd("context", "use", "test-local")
res.Assert(t, icmd.Expected{Out: "test-local"})
t.Run("use", func(t *testing.T) {
res := c.RunDockerCmd("context", "show")
res.Assert(t, icmd.Expected{Out: "test-local"})
res = c.RunDockerCmd("context", "ls")
golden.Assert(t, res.Stdout(), GoldenFile("ls-out-test-local"))
})
var nginxContainerName string
t.Run("run", func(t *testing.T) {
res := c.RunDockerCmd("run", "-d", "-p", "85:80", "nginx")
nginxContainerName = strings.TrimSpace(res.Stdout())
})
defer c.RunDockerOrExitError("rm", "-f", nginxContainerName)
var nginxID string
t.Run("inspect", func(t *testing.T) {
res = c.RunDockerCmd("inspect", nginxContainerName)
inspect := &cmd.ContainerInspectView{}
err := json.Unmarshal([]byte(res.Stdout()), inspect)
assert.NilError(t, err)
nginxID = inspect.ID
})
t.Run("ps", func(t *testing.T) {
res = c.RunDockerCmd("ps")
lines := Lines(res.Stdout())
nginxFound := false
for _, line := range lines {
fields := strings.Fields(line)
if fields[0] == nginxID {
nginxFound = true
assert.Equal(t, fields[1], "nginx")
assert.Equal(t, fields[2], "/docker-entrypoint.sh")
}
}
assert.Assert(t, nginxFound, res.Stdout())
res = c.RunDockerCmd("ps", "--format", "json")
res.Assert(t, icmd.Expected{Out: `"Image":"nginx","Status":"Up Less than a second","Command":"/docker-entrypoint.sh nginx -g 'daemon off;'","Ports":["0.0.0.0:85->80/tcp"`})
res = c.RunDockerCmd("ps", "--quiet")
res.Assert(t, icmd.Expected{Out: nginxID + "\n"})
})
}
func writeDockerfile(t *testing.T) string {
d, err := ioutil.TempDir("", "")
assert.NilError(t, err)

View File

@ -1,3 +1,3 @@
NAME TYPE DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
default moby Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
test-example * example
test-local * local