mirror of https://github.com/docker/compose.git
Return a default implementation when nil
Some backends can decide not to implement a whole set of APIs (compose for example), we now return a default implementation that returns a `errdefs.ErrNotImplemented` for each action making it easy for the cli to print a helpful error message. We also remove any possible nil panics. Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
This commit is contained in:
parent
2df4112913
commit
635ecd7b99
|
@ -20,13 +20,10 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/api/client"
|
||||
apicontext "github.com/docker/api/context"
|
||||
"github.com/docker/api/context/store"
|
||||
"github.com/docker/api/errdefs"
|
||||
)
|
||||
|
||||
|
@ -67,26 +64,10 @@ func Command() *cobra.Command {
|
|||
}
|
||||
|
||||
func checkComposeSupport(ctx context.Context) error {
|
||||
c, err := client.New(ctx)
|
||||
if err == nil {
|
||||
composeService := c.ComposeService()
|
||||
if composeService == nil {
|
||||
return errors.New("compose not implemented in current context")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
_, err := client.New(ctx)
|
||||
if errdefs.IsNotFoundError(err) {
|
||||
currentContext := apicontext.CurrentContext(ctx)
|
||||
s := store.ContextStore(ctx)
|
||||
cc, err := s.Get(currentContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cc.Type() == store.AwsContextType {
|
||||
return errors.Errorf(`%q context type has been renamed. Recreate the context by running:
|
||||
$ docker context create %s <name>`, cc.Type(), store.EcsContextType)
|
||||
}
|
||||
return errors.Wrapf(errdefs.ErrNotImplemented, "compose command not supported on context type %q", cc.Type())
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package compose
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
|
@ -47,16 +46,11 @@ func runDown(ctx context.Context, opts composeOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
composeService := c.ComposeService()
|
||||
if composeService == nil {
|
||||
return errors.New("compose not implemented in current context")
|
||||
}
|
||||
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
options, err := opts.toProjectOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return composeService.Down(ctx, options)
|
||||
return c.ComposeService().Down(ctx, options)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package compose
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -47,14 +46,9 @@ func runLogs(ctx context.Context, opts composeOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
composeService := c.ComposeService()
|
||||
if composeService == nil {
|
||||
return errors.New("compose not implemented in current context")
|
||||
}
|
||||
|
||||
options, err := opts.toProjectOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return composeService.Logs(ctx, options, os.Stdout)
|
||||
return c.ComposeService().Logs(ctx, options, os.Stdout)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package compose
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -51,16 +50,11 @@ func runPs(ctx context.Context, opts composeOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
composeService := c.ComposeService()
|
||||
if composeService == nil {
|
||||
return errors.New("compose not implemented in current context")
|
||||
}
|
||||
|
||||
options, err := opts.toProjectOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serviceList, err := composeService.Ps(ctx, options)
|
||||
serviceList, err := c.ComposeService().Ps(ctx, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package compose
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
|
@ -49,16 +48,11 @@ func runUp(ctx context.Context, opts composeOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
composeService := c.ComposeService()
|
||||
if composeService == nil {
|
||||
return errors.New("compose not implemented in current context")
|
||||
}
|
||||
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
options, err := opts.toProjectOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return composeService.Up(ctx, options)
|
||||
return c.ComposeService().Up(ctx, options)
|
||||
})
|
||||
}
|
||||
|
|
22
cli/main.go
22
cli/main.go
|
@ -175,6 +175,11 @@ func main() {
|
|||
ctype = cc.Type()
|
||||
}
|
||||
|
||||
if ctype == store.AwsContextType {
|
||||
exit(root, currentContext, errors.Errorf(`%q context type has been renamed. Recreate the context by running:
|
||||
$ docker context create %s <name>`, cc.Type(), store.EcsContextType))
|
||||
}
|
||||
|
||||
metrics.Track(ctype, os.Args[1:], root.PersistentFlags())
|
||||
|
||||
ctx = apicontext.WithCurrentContext(ctx, currentContext)
|
||||
|
@ -189,21 +194,32 @@ func main() {
|
|||
// Context should always be handled by new CLI
|
||||
requiredCmd, _, _ := root.Find(os.Args[1:])
|
||||
if requiredCmd != nil && isOwnCommand(requiredCmd) {
|
||||
exit(err)
|
||||
exit(root, currentContext, err)
|
||||
}
|
||||
mobycli.ExecIfDefaultCtxType(ctx)
|
||||
|
||||
checkIfUnknownCommandExistInDefaultContext(err, currentContext)
|
||||
|
||||
exit(err)
|
||||
exit(root, currentContext, err)
|
||||
}
|
||||
}
|
||||
|
||||
func exit(err error) {
|
||||
func exit(cmd *cobra.Command, ctx string, err error) {
|
||||
if errors.Is(err, errdefs.ErrLoginRequired) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(errdefs.ExitCodeLoginRequired)
|
||||
}
|
||||
if errors.Is(err, errdefs.ErrNotImplemented) {
|
||||
cmd, _, _ := cmd.Traverse(os.Args[1:])
|
||||
name := cmd.Name()
|
||||
parent := cmd.Parent()
|
||||
if parent != nil && parent.Parent() != nil {
|
||||
name = parent.Name() + " " + name
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s)\n", name, ctx)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,14 +19,13 @@ package client
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/api/secrets"
|
||||
|
||||
"github.com/docker/api/backend"
|
||||
"github.com/docker/api/compose"
|
||||
"github.com/docker/api/containers"
|
||||
apicontext "github.com/docker/api/context"
|
||||
"github.com/docker/api/context/cloud"
|
||||
"github.com/docker/api/context/store"
|
||||
"github.com/docker/api/secrets"
|
||||
)
|
||||
|
||||
// New returns a backend client associated with current context
|
||||
|
@ -63,15 +62,27 @@ type Client struct {
|
|||
|
||||
// ContainerService returns the backend service for the current context
|
||||
func (c *Client) ContainerService() containers.Service {
|
||||
return c.bs.ContainerService()
|
||||
if cs := c.bs.ContainerService(); cs != nil {
|
||||
return cs
|
||||
}
|
||||
|
||||
return &containerService{}
|
||||
}
|
||||
|
||||
// ComposeService returns the backend service for the current context
|
||||
func (c *Client) ComposeService() compose.Service {
|
||||
return c.bs.ComposeService()
|
||||
if cs := c.bs.ComposeService(); cs != nil {
|
||||
return cs
|
||||
}
|
||||
|
||||
return &composeService{}
|
||||
}
|
||||
|
||||
// SecretsService returns the backend service for the current context
|
||||
func (c *Client) SecretsService() secrets.Service {
|
||||
return c.bs.SecretsService()
|
||||
if ss := c.bs.SecretsService(); ss != nil {
|
||||
return ss
|
||||
}
|
||||
|
||||
return &secretsService{}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright 2020 Docker, Inc.
|
||||
|
||||
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 client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
|
||||
"github.com/docker/api/compose"
|
||||
"github.com/docker/api/errdefs"
|
||||
)
|
||||
|
||||
type composeService struct {
|
||||
}
|
||||
|
||||
// Up executes the equivalent to a `compose up`
|
||||
func (c *composeService) Up(context.Context, *cli.ProjectOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Down executes the equivalent to a `compose down`
|
||||
func (c *composeService) Down(context.Context, *cli.ProjectOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Logs executes the equivalent to a `compose logs`
|
||||
func (c *composeService) Logs(context.Context, *cli.ProjectOptions, io.Writer) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Ps executes the equivalent to a `compose ps`
|
||||
func (c *composeService) Ps(context.Context, *cli.ProjectOptions) ([]compose.ServiceStatus, error) {
|
||||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Convert translate compose model into backend's native format
|
||||
func (c *composeService) Convert(context.Context, *cli.ProjectOptions) ([]byte, error) {
|
||||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
Copyright 2020 Docker, Inc.
|
||||
|
||||
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 client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/api/containers"
|
||||
"github.com/docker/api/errdefs"
|
||||
)
|
||||
|
||||
type containerService struct {
|
||||
}
|
||||
|
||||
// List returns all the containers
|
||||
func (c *containerService) List(context.Context, bool) ([]containers.Container, error) {
|
||||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Start starts a stopped container
|
||||
func (c *containerService) Start(context.Context, string) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Stop stops the running container
|
||||
func (c *containerService) Stop(context.Context, string, *uint32) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Run creates and starts a container
|
||||
func (c *containerService) Run(context.Context, containers.ContainerConfig) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Exec executes a command inside a running container
|
||||
func (c *containerService) Exec(context.Context, string, containers.ExecRequest) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Logs returns all the logs of a container
|
||||
func (c *containerService) Logs(context.Context, string, containers.LogsRequest) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Delete removes containers
|
||||
func (c *containerService) Delete(context.Context, string, containers.DeleteRequest) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
// Inspect get a specific container
|
||||
func (c *containerService) Inspect(context.Context, string) (containers.Container, error) {
|
||||
return containers.Container{}, errdefs.ErrNotImplemented
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2020 Docker, Inc.
|
||||
|
||||
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 client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/api/errdefs"
|
||||
"github.com/docker/api/secrets"
|
||||
)
|
||||
|
||||
type secretsService struct {
|
||||
}
|
||||
|
||||
func (s *secretsService) CreateSecret(context.Context, secrets.Secret) (string, error) {
|
||||
return "", errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (s *secretsService) InspectSecret(context.Context, string) (secrets.Secret, error) {
|
||||
return secrets.Secret{}, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (s *secretsService) ListSecrets(context.Context) ([]secrets.Secret, error) {
|
||||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (s *secretsService) DeleteSecret(context.Context, string, bool) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
|
@ -54,7 +54,7 @@ func TestComposeNotImplemented(t *testing.T) {
|
|||
res = c.RunDockerCmd("compose", "up")
|
||||
res.Assert(t, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: `compose command not supported on context type "moby": not implemented`,
|
||||
Err: `Command "compose up" not available in current context (default)`,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue