From 370283958ab60553d0b6cab6f701d7d94f192bed Mon Sep 17 00:00:00 2001 From: Harsh4902 Date: Wed, 2 Apr 2025 21:43:07 +0530 Subject: [PATCH 1/2] fixes #108 feat: Add support for start and stop command Signed-off-by: Harsh4902 --- cmd/cmd.go | 2 + cmd/start.go | 143 +++++++++++++++++++++++++++++++++++++++++++ cmd/stop.go | 69 +++++++++++++++++++++ go.mod | 39 +++++++++++- go.sum | 136 ++++++++++++++++++++++++++++++++++++++++ pkg/config/config.go | 76 +++++++++++++++++++++++ 6 files changed, 463 insertions(+), 2 deletions(-) create mode 100644 cmd/start.go create mode 100644 cmd/stop.go diff --git a/cmd/cmd.go b/cmd/cmd.go index 2cb13b01..07023782 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -46,4 +46,6 @@ func init() { rootCmd.AddCommand(NewVersionCommand()) rootCmd.AddCommand(NewTestCommand()) rootCmd.AddCommand(NewImportURLCommand()) + rootCmd.AddCommand(NewStartCommand()) + rootCmd.AddCommand(NewStopCommand()) } diff --git a/cmd/start.go b/cmd/start.go new file mode 100644 index 00000000..8f77fa10 --- /dev/null +++ b/cmd/start.go @@ -0,0 +1,143 @@ +package cmd + +import ( + "context" + "fmt" + "io" + "log" + "os" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/image" + "github.com/docker/docker/client" + "github.com/docker/go-connections/nat" + "github.com/microcks/microcks-cli/pkg/config" + "github.com/spf13/cobra" +) + +func NewStartCommand() *cobra.Command { + var ( + hostIP string = "0.0.0.0" + name string + hostPort string + imageName string + autoRemove bool + ) + var startCmd = &cobra.Command{ + Use: "start", + Short: "start microcks instance", + Example: `# Start a Microcks instance +microcks start + +# Define your port (by default 8585) +microcks start --port [Port you want]`, + Run: func(cmd *cobra.Command, args []string) { + cfg, err := config.EnsureConfig(config.ConfigPath) + + if err != nil { + log.Fatalf("Error loading config: %v", err) + } + + if cfg.Instance.Status == "Running" { + fmt.Println("Microcks is already running.") + return + } + + if cfg.Instance.Status == "Stopped" { + if err := startContainer(cfg.Instance.ContainerID); err != nil { + fmt.Errorf("failed to start container: %v", err) + } + fmt.Println("Microcks started successfully...") + return + } + + cfg.Instance.Name = name + cfg.Instance.Image = imageName + cfg.Instance.Port = hostPort + cfg.Instance.AutoRemove = autoRemove + + containerID, err := createContainer(cfg, hostIP) + + if err != nil { + log.Fatalf("Failed to create a container: %v", err) + } + cfg.Instance.ContainerID = containerID + + if err := startContainer(cfg.Instance.ContainerID); err != nil { + fmt.Errorf("failed to start container: %v", err) + } + + err = config.SaveConfig(config.ConfigPath, cfg) + + if err != nil { + log.Fatalf("Failed to save config: %v", err) + } + + fmt.Printf("Microcks started successfully...") + }, + } + startCmd.Flags().StringVar(&name, "name", "microcks", "name for you microcks instance") + startCmd.Flags().StringVar(&hostPort, "port", "8585", "") + startCmd.Flags().StringVar(&imageName, "image", "quay.io/microcks/microcks-uber:latest-native", "image which will be used to create a container") + startCmd.Flags().BoolVar(&autoRemove, "rm", false, "mimic of '--rm' flag of dokcer to automatically remove the container when it exits") + return startCmd +} + +func createContainer(cfg *config.Config, hostIP string) (string, error) { + ctx := context.Background() + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return "", err + } + defer cli.Close() + + // Define exposed port and bindings + exposedPort, _ := nat.NewPort("tcp", "8080") + portBindings := nat.PortMap{ + exposedPort: []nat.PortBinding{ + { + HostIP: hostIP, + HostPort: cfg.Instance.Port, + }, + }, + } + + out, err := cli.ImagePull(ctx, cfg.Instance.Image, image.PullOptions{}) + if err != nil { + return "", err + } + defer out.Close() + io.Copy(os.Stdout, out) + + resp, err := cli.ContainerCreate( + ctx, + &container.Config{ + Image: cfg.Instance.Image, + ExposedPorts: nat.PortSet{exposedPort: struct{}{}}, + }, + &container.HostConfig{ + PortBindings: portBindings, + AutoRemove: cfg.Instance.AutoRemove, + }, nil, nil, cfg.Instance.Name) + + if err != nil { + return "", err + } + + return resp.ID, nil +} + +func startContainer(cotainerID string) error { + ctx := context.Background() + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return err + } + defer cli.Close() + + if err := cli.ContainerStart(ctx, cotainerID, container.StartOptions{}); err != nil { + panic(err) + } + + return nil +} diff --git a/cmd/stop.go b/cmd/stop.go new file mode 100644 index 00000000..43ebacf7 --- /dev/null +++ b/cmd/stop.go @@ -0,0 +1,69 @@ +package cmd + +import ( + "context" + "fmt" + "log" + + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + "github.com/microcks/microcks-cli/pkg/config" + "github.com/spf13/cobra" +) + +func NewStopCommand() *cobra.Command { + + var stopCmd = &cobra.Command{ + Use: "stop", + Short: "stop microcks instance", + Long: "stop microcks instance", + Run: func(cmd *cobra.Command, args []string) { + + cfg, err := config.LoadConfig(config.ConfigPath) + if err != nil { + log.Fatalf("Failed to load config: %v", err) + } + + stopContainer(cfg.Instance.ContainerID) + + cfg.Instance.Status = "Stopped" + + if cfg.Instance.AutoRemove { + cfg.Instance = struct { + Name string "yaml:\"name\"" + Image string "yaml:\"image\"" + Status string "yaml:\"status\"" + Port string "yaml:\"port\"" + ContainerID string "yaml:\"containerID\"" + AutoRemove bool "yaml:\"autoRemove\"" + }{} + } + + err = config.SaveConfig(config.ConfigPath, cfg) + + if err != nil { + log.Fatalf("Failed to save config: %v", err) + } + + fmt.Println("Microcks stopped successfully...") + }, + } + + return stopCmd +} + +func stopContainer(containerId string) { + ctx := context.Background() + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + panic(err) + } + defer cli.Close() + + fmt.Print("Stopping container ", containerId, "... ") + noWaitTimeout := 0 // to not wait for the container to exit gracefully + if err := cli.ContainerStop(ctx, containerId, containertypes.StopOptions{Timeout: &noWaitTimeout}); err != nil { + panic(err) + } + fmt.Println("Success") +} diff --git a/go.mod b/go.mod index 97941618..d366b664 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,40 @@ module github.com/microcks/microcks-cli -go 1.12 +go 1.23.0 -require github.com/spf13/cobra v1.9.1 +toolchain go1.24.1 + +require ( + github.com/docker/docker v28.0.4+incompatible + github.com/docker/go-connections v0.5.0 + github.com/spf13/cobra v1.9.1 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/Microsoft/go-winio v0.4.14 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/term v0.5.2 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/time v0.11.0 // indirect + gotest.tools/v3 v3.5.2 // indirect +) diff --git a/go.sum b/go.sum index ffae55e3..7f448527 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,146 @@ +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok= +github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= +go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= +google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= +google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= diff --git a/pkg/config/config.go b/pkg/config/config.go index 5d8ff4b7..1c688c49 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -22,7 +22,11 @@ import ( "io/ioutil" "net/http" "net/http/httputil" + "os" + "path/filepath" strings "strings" + + "gopkg.in/yaml.v2" ) var ( @@ -32,8 +36,21 @@ var ( CaCertPaths string // Verbose represents a debug flag for HTTP Exchanges Verbose bool = false + + ConfigPath = filepath.Join(os.Getenv("HOME"), ".microcks-cli", "config.yaml") ) +type Config struct { + Instance struct { + Name string `yaml:"name"` + Image string `yaml:"image"` + Status string `yaml:"status"` + Port string `yaml:"port"` + ContainerID string `yaml:"containerID"` + AutoRemove bool `yaml:"autoRemove"` + } `yaml:"instance"` +} + // CreateTLSConfig wraps the creation of tls.Config object for use with HTTP Client for example. func CreateTLSConfig() *tls.Config { tlsConfig := &tls.Config{} @@ -91,3 +108,62 @@ func DumpResponseIfRequired(name string, resp *http.Response, body bool) { } } } + +//Functions related to configs + +func defaultConfig() *Config { + return &Config{ + Instance: struct { + Name string `yaml:"name"` + Image string `yaml:"image"` + Status string `yaml:"status"` + Port string `yaml:"port"` + ContainerID string `yaml:"containerID"` + AutoRemove bool `yaml:"autoRemove"` + }{ + Name: "microcks", + Image: "", + Status: "", + Port: "", + AutoRemove: false, + }, + } +} + +func EnsureConfig(path string) (*Config, error) { + if _, err := os.Stat(path); os.IsNotExist(err) { + fmt.Println("Config not found. Initializing default config.") + cfg := defaultConfig() + err := SaveConfig(path, cfg) + if err != nil { + return nil, fmt.Errorf("failed to create default config: %w", err) + } + return cfg, nil + } + return LoadConfig(path) +} + +func LoadConfig(path string) (*Config, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + var cfg Config + err = yaml.Unmarshal(data, &cfg) + if err != nil { + return nil, err + } + return &cfg, nil +} + +func SaveConfig(path string, cfg *Config) error { + err := os.MkdirAll(filepath.Dir(path), 0755) + if err != nil { + return err + } + data, err := yaml.Marshal(cfg) + if err != nil { + return err + } + return os.WriteFile(path, data, 0644) +} From 60b7dee43a1f0135a89b95edab4a9c33dc4ceb67 Mon Sep 17 00:00:00 2001 From: Harsh4902 Date: Mon, 21 Apr 2025 21:34:57 +0530 Subject: [PATCH 2/2] fixes #108 feat:Add --driver flag to specify container engine for microcks instance Signed-off-by: Harsh4902 --- cmd/cmd.go | 2 +- cmd/start.go | 70 ++++++++++++++++++++++++++++++++++---------- cmd/stop.go | 17 ++++++----- pkg/config/config.go | 3 ++ 4 files changed, 68 insertions(+), 24 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 07023782..f1747263 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -23,7 +23,7 @@ import ( ) var rootCmd = &cobra.Command{ - Use: "microcks-cli", + Use: "microcks", Short: "A CLI tool for Microcks", Long: `microcks-cli is a CLI for interacting with Microcks server APIs. It allows to launch tests or import API artifacts with minimal dependencies.`, diff --git a/cmd/start.go b/cmd/start.go index 8f77fa10..56a7bb33 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -6,6 +6,8 @@ import ( "io" "log" "os" + "os/exec" + "strings" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/image" @@ -22,6 +24,7 @@ func NewStartCommand() *cobra.Command { hostPort string imageName string autoRemove bool + driver string ) var startCmd = &cobra.Command{ Use: "start", @@ -30,7 +33,13 @@ func NewStartCommand() *cobra.Command { microcks start # Define your port (by default 8585) -microcks start --port [Port you want]`, +microcks start --port [Port you want] + +# Define your driver (by default docker) +microcks start --driver [driver you wnat either 'docker' or 'podman'] + +# Define name of your microcks container/instance +microcks start --name [name of you container/instance]`, Run: func(cmd *cobra.Command, args []string) { cfg, err := config.EnsureConfig(config.ConfigPath) @@ -38,13 +47,24 @@ microcks start --port [Port you want]`, log.Fatalf("Error loading config: %v", err) } + cfg.Instance.Driver = driver + + cli, err := createClient(cfg.Instance.Driver) + + if err != nil { + fmt.Println(err) + return + } + + defer cli.Close() + if cfg.Instance.Status == "Running" { fmt.Println("Microcks is already running.") return } - if cfg.Instance.Status == "Stopped" { - if err := startContainer(cfg.Instance.ContainerID); err != nil { + if cfg.Instance.Status == "Stopped" || cfg.Instance.Status == "Created" { + if err := startContainer(cfg.Instance.ContainerID, cli); err != nil { fmt.Errorf("failed to start container: %v", err) } fmt.Println("Microcks started successfully...") @@ -56,21 +76,25 @@ microcks start --port [Port you want]`, cfg.Instance.Port = hostPort cfg.Instance.AutoRemove = autoRemove - containerID, err := createContainer(cfg, hostIP) + containerID, err := createContainer(cfg, hostIP, cli) if err != nil { log.Fatalf("Failed to create a container: %v", err) + return } cfg.Instance.ContainerID = containerID + cfg.Instance.Status = "Created" - if err := startContainer(cfg.Instance.ContainerID); err != nil { + if err := startContainer(cfg.Instance.ContainerID, cli); err != nil { fmt.Errorf("failed to start container: %v", err) + return } - + cfg.Instance.Status = "Running" err = config.SaveConfig(config.ConfigPath, cfg) if err != nil { log.Fatalf("Failed to save config: %v", err) + return } fmt.Printf("Microcks started successfully...") @@ -80,16 +104,35 @@ microcks start --port [Port you want]`, startCmd.Flags().StringVar(&hostPort, "port", "8585", "") startCmd.Flags().StringVar(&imageName, "image", "quay.io/microcks/microcks-uber:latest-native", "image which will be used to create a container") startCmd.Flags().BoolVar(&autoRemove, "rm", false, "mimic of '--rm' flag of dokcer to automatically remove the container when it exits") + startCmd.Flags().StringVar(&driver, "driver", "docker", "use --driver to change driver from docker to podman") return startCmd } -func createContainer(cfg *config.Config, hostIP string) (string, error) { - ctx := context.Background() +func createClient(driver string) (*client.Client, error) { + + if driver != "docker" { + out, err := exec.Command("podman", "machine", "inspect", "--format", "{{.ConnectionInfo.PodmanSocket.Path}}").Output() + if err != nil { + fmt.Println(err) + } + + err = os.Setenv("DOCKER_HOST", "unix://"+strings.TrimSpace(string(out))) + if err != nil { + fmt.Println(err) + } + } + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { - return "", err + return nil, err } - defer cli.Close() + + return cli, nil +} + +func createContainer(cfg *config.Config, hostIP string, cli *client.Client) (string, error) { + ctx := context.Background() // Define exposed port and bindings exposedPort, _ := nat.NewPort("tcp", "8080") @@ -127,13 +170,8 @@ func createContainer(cfg *config.Config, hostIP string) (string, error) { return resp.ID, nil } -func startContainer(cotainerID string) error { +func startContainer(cotainerID string, cli *client.Client) error { ctx := context.Background() - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { - return err - } - defer cli.Close() if err := cli.ContainerStart(ctx, cotainerID, container.StartOptions{}); err != nil { panic(err) diff --git a/cmd/stop.go b/cmd/stop.go index 43ebacf7..e943b21e 100644 --- a/cmd/stop.go +++ b/cmd/stop.go @@ -24,7 +24,14 @@ func NewStopCommand() *cobra.Command { log.Fatalf("Failed to load config: %v", err) } - stopContainer(cfg.Instance.ContainerID) + cli, err := createClient(cfg.Instance.Driver) + + if err != nil { + fmt.Println(err) + return + } + + stopContainer(cfg.Instance.ContainerID, cli) cfg.Instance.Status = "Stopped" @@ -36,6 +43,7 @@ func NewStopCommand() *cobra.Command { Port string "yaml:\"port\"" ContainerID string "yaml:\"containerID\"" AutoRemove bool "yaml:\"autoRemove\"" + Driver string "yaml:\"driver\"" }{} } @@ -52,13 +60,8 @@ func NewStopCommand() *cobra.Command { return stopCmd } -func stopContainer(containerId string) { +func stopContainer(containerId string, cli *client.Client) { ctx := context.Background() - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { - panic(err) - } - defer cli.Close() fmt.Print("Stopping container ", containerId, "... ") noWaitTimeout := 0 // to not wait for the container to exit gracefully diff --git a/pkg/config/config.go b/pkg/config/config.go index 1c688c49..cfdb087f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -48,6 +48,7 @@ type Config struct { Port string `yaml:"port"` ContainerID string `yaml:"containerID"` AutoRemove bool `yaml:"autoRemove"` + Driver string `yaml:"driver"` } `yaml:"instance"` } @@ -120,12 +121,14 @@ func defaultConfig() *Config { Port string `yaml:"port"` ContainerID string `yaml:"containerID"` AutoRemove bool `yaml:"autoRemove"` + Driver string `yaml:"driver"` }{ Name: "microcks", Image: "", Status: "", Port: "", AutoRemove: false, + Driver: "docker", }, } }