2020-04-24 18:04:32 +02:00
/ *
Copyright ( c ) 2020 Docker Inc .
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 .
* /
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-05-04 19:50:01 +02:00
_ "github.com/docker/api/moby"
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-06 22:22:30 +02:00
"github.com/docker/api/cli/dockerclassic"
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-05-12 14:35:16 +02:00
var (
runningOwnCommand bool
2020-05-29 17:07:48 +02:00
ownCommands = map [ string ] struct { } {
"context" : { } ,
"login" : { } ,
"serve" : { } ,
}
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" ,
Long : "docker for the 2020s" ,
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-05-12 14:35:16 +02:00
runningOwnCommand = isOwnCommand ( cmd )
if ! runningOwnCommand {
2020-05-29 12:02:07 +02:00
dockerclassic . Exec ( 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-05-05 10:58:24 +02:00
compose . Command ( ) ,
2020-05-29 17:07:48 +02:00
login . Command ( ) ,
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-05-12 14:35:16 +02:00
runningOwnCommand = isOwnCommand ( cmd )
if ! runningOwnCommand {
2020-05-29 12:02:07 +02:00
dockerclassic . Exec ( cmd . Context ( ) )
2020-04-26 22:13:35 +02:00
}
2020-04-24 18:04:32 +02:00
helpFunc ( cmd , args )
} )
2020-05-14 18:29:09 +02:00
root . PersistentFlags ( ) . BoolVarP ( & opts . Debug , "debug" , "d" , false , "enable debug output in the logs" )
2020-05-14 09:48:53 +02:00
opts . AddConfigFlags ( root . PersistentFlags ( ) )
opts . AddContextFlags ( root . PersistentFlags ( ) )
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-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-05-20 15:55:05 +02:00
currentContext , err := determineCurrentContext ( opts . Context , configDir )
2020-04-29 23:10:06 +02:00
if err != nil {
2020-05-18 14:38:21 +02:00
fatal ( errors . New ( "unable to determine current context" ) )
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-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 )
if err = root . ExecuteContext ( ctx ) ; err != nil {
2020-05-12 14:35:16 +02:00
// Context should always be handled by new CLI
if runningOwnCommand {
fmt . Fprintln ( os . Stderr , err )
os . Exit ( 1 )
}
2020-05-29 12:02:07 +02:00
dockerclassic . Exec ( ctx )
2020-06-05 17:30:27 +02:00
checkIfUnknownCommandExistInDefaultContext ( err , currentContext )
fmt . Fprintln ( os . Stderr , err )
2020-04-24 18:04:32 +02:00
os . Exit ( 1 )
}
}
2020-06-05 17:30:27 +02:00
func checkIfUnknownCommandExistInDefaultContext ( err error , currentContext string ) {
re := regexp . MustCompile ( ` unknown command "([^"]*)" ` )
submatch := re . FindSubmatch ( [ ] byte ( err . Error ( ) ) )
if len ( submatch ) == 2 {
dockerCommand := string ( submatch [ 1 ] )
if dockerclassic . IsDefaultContextCommand ( dockerCommand ) {
fmt . Fprintf ( os . Stderr , "Command \"%s\" not available in current context (%s), you can use the \"default\" context to run this command\n" , dockerCommand , currentContext )
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-05-18 14:38:21 +02:00
func determineCurrentContext ( flag string , configDir string ) ( string , error ) {
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 {
return "" , err
}
res = config . CurrentContext
}
if res == "" {
res = "default"
}
return res , nil
}
2020-05-14 09:59:12 +02:00
func fatal ( err error ) {
fmt . Fprint ( os . Stderr , err )
os . Exit ( 1 )
}