2020-04-24 18:04:32 +02:00
/ *
2020-06-18 16:13:24 +02:00
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 .
2020-04-24 18:04:32 +02:00
* /
package main
import (
"context"
"fmt"
2020-05-22 10:45:01 +02:00
"math/rand"
2020-04-24 18:04:32 +02:00
"os"
2020-05-13 10:24:16 +02:00
"os/signal"
2020-04-24 18:04:32 +02:00
"path/filepath"
2020-06-05 17:30:27 +02:00
"regexp"
2020-05-13 10:24:16 +02:00
"syscall"
2020-05-22 10:45:01 +02:00
"time"
2020-04-24 18:04:32 +02:00
2020-05-14 09:59:12 +02:00
"github.com/pkg/errors"
2020-05-02 18:54:03 +02:00
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
2020-05-04 19:50:01 +02:00
// Backend registrations
2020-05-05 15:56:00 +02:00
_ "github.com/docker/api/azure"
_ "github.com/docker/api/example"
2020-06-12 15:00:30 +02:00
_ "github.com/docker/api/local"
2020-06-22 09:55:28 +02:00
"github.com/docker/api/metrics"
2020-05-05 15:56:00 +02:00
2020-04-24 18:04:32 +02:00
"github.com/docker/api/cli/cmd"
2020-05-05 15:56:00 +02:00
"github.com/docker/api/cli/cmd/compose"
2020-05-14 20:55:40 +02:00
contextcmd "github.com/docker/api/cli/cmd/context"
2020-06-06 22:22:30 +02:00
"github.com/docker/api/cli/cmd/login"
2020-05-04 10:28:42 +02:00
"github.com/docker/api/cli/cmd/run"
2020-06-17 17:57:44 +02:00
"github.com/docker/api/cli/mobycli"
2020-05-14 18:29:09 +02:00
cliopts "github.com/docker/api/cli/options"
2020-06-06 22:22:30 +02:00
"github.com/docker/api/config"
2020-04-24 18:04:32 +02:00
apicontext "github.com/docker/api/context"
"github.com/docker/api/context/store"
)
2020-06-23 17:14:59 +02:00
var (
version = "dev"
)
2020-05-12 14:35:16 +02:00
var (
2020-06-10 17:24:13 +02:00
ownCommands = map [ string ] struct { } {
2020-05-29 17:07:48 +02:00
"context" : { } ,
"login" : { } ,
"serve" : { } ,
2020-06-12 17:54:14 +02:00
"version" : { } ,
2020-05-29 17:07:48 +02:00
}
2020-06-26 10:23:37 +02:00
unknownCommandRegexp = regexp . MustCompile ( ` unknown command "([^"]*)" ` )
2020-05-12 14:35:16 +02:00
)
2020-04-24 18:04:32 +02:00
func init ( ) {
// initial hack to get the path of the project's bin dir
// into the env of this cli for development
path , err := filepath . Abs ( filepath . Dir ( os . Args [ 0 ] ) )
if err != nil {
2020-05-14 09:59:12 +02:00
fatal ( errors . Wrap ( err , "unable to get absolute bin path" ) )
2020-04-24 18:04:32 +02:00
}
if err := os . Setenv ( "PATH" , fmt . Sprintf ( "%s:%s" , os . Getenv ( "PATH" ) , path ) ) ; err != nil {
panic ( err )
}
2020-05-22 10:45:01 +02:00
// Seed random
rand . Seed ( time . Now ( ) . UnixNano ( ) )
2020-04-24 18:04:32 +02:00
}
2020-04-29 23:39:54 +02:00
func isOwnCommand ( cmd * cobra . Command ) bool {
2020-04-26 22:13:35 +02:00
if cmd == nil {
return false
}
2020-05-29 17:07:48 +02:00
if _ , ok := ownCommands [ cmd . Name ( ) ] ; ok {
2020-04-26 22:13:35 +02:00
return true
}
2020-04-29 23:39:54 +02:00
return isOwnCommand ( cmd . Parent ( ) )
2020-04-26 22:13:35 +02:00
}
2020-04-24 18:04:32 +02:00
func main ( ) {
2020-05-14 18:29:09 +02:00
var opts cliopts . GlobalOpts
2020-04-24 18:04:32 +02:00
root := & cobra . Command {
2020-04-27 11:32:16 +02:00
Use : "docker" ,
SilenceErrors : true ,
2020-05-03 13:35:25 +02:00
SilenceUsage : true ,
2020-04-24 18:04:32 +02:00
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
2020-06-10 17:24:13 +02:00
if ! isOwnCommand ( cmd ) {
2020-06-18 09:29:01 +02:00
mobycli . ExecIfDefaultCtxType ( cmd . Context ( ) )
2020-04-26 22:13:35 +02:00
}
2020-04-24 18:04:32 +02:00
return nil
} ,
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
return cmd . Help ( )
} ,
}
2020-05-01 15:28:44 +02:00
root . AddCommand (
2020-05-20 15:55:05 +02:00
contextcmd . Command ( ) ,
2020-05-06 09:37:52 +02:00
cmd . PsCommand ( ) ,
2020-05-01 15:28:44 +02:00
cmd . ServeCommand ( ) ,
2020-05-04 10:28:42 +02:00
run . Command ( ) ,
2020-05-03 13:35:25 +02:00
cmd . ExecCommand ( ) ,
2020-05-03 13:41:45 +02:00
cmd . LogsCommand ( ) ,
2020-05-10 22:37:28 +02:00
cmd . RmCommand ( ) ,
2020-06-12 10:53:06 +02:00
cmd . InspectCommand ( ) ,
2020-05-05 10:58:24 +02:00
compose . Command ( ) ,
2020-05-29 17:07:48 +02:00
login . Command ( ) ,
2020-06-23 17:14:59 +02:00
cmd . VersionCommand ( version ) ,
2020-05-01 15:28:44 +02:00
)
2020-04-24 18:04:32 +02:00
helpFunc := root . HelpFunc ( )
root . SetHelpFunc ( func ( cmd * cobra . Command , args [ ] string ) {
2020-06-10 17:24:13 +02:00
if ! isOwnCommand ( cmd ) {
2020-06-18 09:29:01 +02:00
mobycli . ExecIfDefaultCtxType ( cmd . Context ( ) )
2020-04-26 22:13:35 +02:00
}
2020-04-24 18:04:32 +02:00
helpFunc ( cmd , args )
} )
2020-06-22 13:11:08 +02:00
root . PersistentFlags ( ) . BoolVarP ( & opts . Debug , "debug" , "D" , false , "enable debug output in the logs" )
2020-06-23 11:27:19 +02:00
root . PersistentFlags ( ) . StringVarP ( & opts . Host , "host" , "H" , "" , "Daemon socket(s) to connect to" )
2020-05-14 09:48:53 +02:00
opts . AddConfigFlags ( root . PersistentFlags ( ) )
opts . AddContextFlags ( root . PersistentFlags ( ) )
2020-06-23 15:13:43 +02:00
root . Flags ( ) . BoolVarP ( & opts . Version , "version" , "v" , false , "Print version information and quit" )
2020-04-24 18:04:32 +02:00
// populate the opts with the global flags
_ = root . PersistentFlags ( ) . Parse ( os . Args [ 1 : ] )
2020-05-14 18:29:09 +02:00
if opts . Debug {
2020-04-24 18:04:32 +02:00
logrus . SetLevel ( logrus . DebugLevel )
}
2020-05-13 10:24:16 +02:00
ctx , cancel := newSigContext ( )
2020-04-24 18:04:32 +02:00
defer cancel ( )
2020-06-26 10:23:37 +02:00
// --host and --version should immediately be forwarded to the original cli
if opts . Host != "" || opts . Version {
mobycli . Exec ( ctx )
2020-06-23 15:13:43 +02:00
}
2020-06-23 11:27:19 +02:00
2020-05-14 18:29:09 +02:00
if opts . Config == "" {
fatal ( errors . New ( "config path cannot be empty" ) )
}
2020-05-20 15:55:05 +02:00
configDir := opts . Config
2020-06-06 22:22:30 +02:00
ctx = config . WithDir ( ctx , configDir )
2020-05-18 14:38:21 +02:00
2020-06-15 10:04:01 +02:00
currentContext := determineCurrentContext ( opts . Context , configDir )
2020-04-29 23:10:06 +02:00
2020-05-20 15:55:05 +02:00
s , err := store . New ( store . WithRoot ( configDir ) )
2020-04-24 18:04:32 +02:00
if err != nil {
2020-05-14 09:59:12 +02:00
fatal ( errors . Wrap ( err , "unable to create context store" ) )
2020-04-24 18:04:32 +02:00
}
2020-06-22 09:55:28 +02:00
ctype := store . DefaultContextType
cc , _ := s . Get ( currentContext )
if cc != nil {
ctype = cc . Type ( )
}
metrics . Track ( ctype , os . Args [ 1 : ] , root . PersistentFlags ( ) )
2020-05-14 09:48:53 +02:00
ctx = apicontext . WithCurrentContext ( ctx , currentContext )
2020-04-24 18:04:32 +02:00
ctx = store . WithContextStore ( ctx , s )
2020-06-26 10:23:37 +02:00
if err = root . ExecuteContext ( ctx ) ; err != nil {
2020-05-12 14:35:16 +02:00
// Context should always be handled by new CLI
2020-06-10 17:24:13 +02:00
requiredCmd , _ , _ := root . Find ( os . Args [ 1 : ] )
if requiredCmd != nil && isOwnCommand ( requiredCmd ) {
2020-06-26 10:23:37 +02:00
fatal ( err )
2020-05-12 14:35:16 +02:00
}
2020-06-18 09:29:01 +02:00
mobycli . ExecIfDefaultCtxType ( ctx )
2020-06-05 17:30:27 +02:00
checkIfUnknownCommandExistInDefaultContext ( err , currentContext )
2020-06-26 10:23:37 +02:00
fatal ( err )
2020-04-24 18:04:32 +02:00
}
}
2020-06-05 17:30:27 +02:00
func checkIfUnknownCommandExistInDefaultContext ( err error , currentContext string ) {
2020-06-26 10:23:37 +02:00
submatch := unknownCommandRegexp . FindSubmatch ( [ ] byte ( err . Error ( ) ) )
2020-06-05 17:30:27 +02:00
if len ( submatch ) == 2 {
dockerCommand := string ( submatch [ 1 ] )
2020-06-17 17:57:44 +02:00
if mobycli . IsDefaultContextCommand ( dockerCommand ) {
2020-06-26 10:23:37 +02:00
fmt . Fprintf ( os . Stderr , "Command %q not available in current context (%s), you can use the \"default\" context to run this command\n" , dockerCommand , currentContext )
2020-06-05 17:30:27 +02:00
os . Exit ( 1 )
}
}
}
2020-05-13 10:24:16 +02:00
func newSigContext ( ) ( context . Context , func ( ) ) {
ctx , cancel := context . WithCancel ( context . Background ( ) )
s := make ( chan os . Signal )
signal . Notify ( s , syscall . SIGTERM , syscall . SIGINT )
go func ( ) {
<- s
cancel ( )
} ( )
return ctx , cancel
}
2020-06-15 10:04:01 +02:00
func determineCurrentContext ( flag string , configDir string ) string {
2020-05-18 14:38:21 +02:00
res := flag
if res == "" {
2020-06-06 22:22:30 +02:00
config , err := config . LoadFile ( configDir )
2020-05-18 14:38:21 +02:00
if err != nil {
2020-06-15 10:04:01 +02:00
fmt . Fprintln ( os . Stderr , errors . Wrap ( err , "WARNING" ) )
return "default"
2020-05-18 14:38:21 +02:00
}
res = config . CurrentContext
}
if res == "" {
res = "default"
}
2020-06-15 10:04:01 +02:00
return res
2020-05-18 14:38:21 +02:00
}
2020-05-14 09:59:12 +02:00
func fatal ( err error ) {
fmt . Fprint ( os . Stderr , err )
os . Exit ( 1 )
}