mirror of https://github.com/docker/compose.git
Use docker/api progress writer
Signed-off-by: aiordache <anca.iordache@docker.com> Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
83d65c02a0
commit
de99add26b
|
@ -12,6 +12,7 @@ import (
|
|||
amazon "github.com/docker/ecs-plugin/pkg/amazon/backend"
|
||||
"github.com/docker/ecs-plugin/pkg/amazon/cloudformation"
|
||||
"github.com/docker/ecs-plugin/pkg/docker"
|
||||
"github.com/docker/ecs-plugin/pkg/progress"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -79,7 +80,11 @@ func UpCommand(dockerCli command.Cli, options *composeOptions) *cobra.Command {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return backend.Up(context.Background(), opts)
|
||||
|
||||
return progress.Run(context.Background(), func(ctx context.Context) error {
|
||||
backend.SetWriter(ctx)
|
||||
return backend.Up(ctx, opts)
|
||||
})
|
||||
}),
|
||||
}
|
||||
cmd.Flags().StringVar(&opts.loadBalancerArn, "load-balancer", "", "")
|
||||
|
@ -124,7 +129,10 @@ func DownCommand(dockerCli command.Cli, options *composeOptions) *cobra.Command
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return backend.Down(context.Background(), opts)
|
||||
return progress.Run(context.Background(), func(ctx context.Context) error {
|
||||
backend.SetWriter(ctx)
|
||||
return backend.Down(ctx, opts)
|
||||
})
|
||||
}),
|
||||
}
|
||||
cmd.Flags().BoolVar(&opts.DeleteCluster, "delete-cluster", false, "Delete cluster")
|
||||
|
@ -139,7 +147,7 @@ func LogsCommand(dockerCli command.Cli, options *composeOptions) *cobra.Command
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return backend.Logs(context.Background(), opts)
|
||||
return backend.Logs(context.Background(), opts, os.Stdout)
|
||||
}),
|
||||
}
|
||||
return cmd
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
module github.com/docker/ecs-plugin
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.7 // indirect
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
|
||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
|
||||
|
@ -10,11 +9,13 @@ require (
|
|||
github.com/bitly/go-hostpool v0.1.0 // indirect
|
||||
github.com/bitly/go-simplejson v0.5.0 // indirect
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129
|
||||
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
|
||||
github.com/bugsnag/panicwrap v1.2.0 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cloudflare/cfssl v1.4.1 // indirect
|
||||
github.com/compose-spec/compose-go v0.0.0-20200811091145-837f8f4de457
|
||||
github.com/containerd/console v1.0.0
|
||||
github.com/containerd/containerd v1.3.2 // indirect
|
||||
github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb // indirect
|
||||
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492
|
||||
|
@ -36,16 +37,17 @@ require (
|
|||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/miekg/pkcs11 v1.0.3 // indirect
|
||||
github.com/mitchellh/mapstructure v1.3.3
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/moby/term v0.0.0-20200611042045-63b9a826fb74
|
||||
github.com/morikuni/aec v1.0.0
|
||||
github.com/onsi/ginkgo v1.11.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/theupdateframework/notary v0.6.1 // indirect
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 // indirect
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
google.golang.org/grpc v1.27.0 // indirect
|
||||
gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect
|
||||
|
|
24
ecs/go.sum
24
ecs/go.sum
|
@ -34,6 +34,8 @@ github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngE
|
|||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 h1:gfAMKE626QEuKG3si0pdTRcr/YEbBoxY+3GOH3gWvl4=
|
||||
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129/go.mod h1:u9UyCz2eTrSGy6fbupqJ54eY5c4IC8gREQ1053dK12U=
|
||||
github.com/bugsnag/bugsnag-go v1.5.3 h1:yeRUT3mUE13jL1tGwvoQsKdVbAsQx9AJ+fqahKveP04=
|
||||
github.com/bugsnag/bugsnag-go v1.5.3/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABFQA=
|
||||
|
@ -54,12 +56,13 @@ github.com/cloudflare/cfssl v1.4.1 h1:vScfU2DrIUI9VPHBVeeAQ0q5A+9yshO1Gz+3QoUQiK
|
|||
github.com/cloudflare/cfssl v1.4.1/go.mod h1:KManx/OJPb5QY+y0+o/898AMcM128sF0bURvoVUSjTo=
|
||||
github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41/go.mod h1:eaZPlJWD+G9wseg1BuRXlHnjntPMrywMsyxf+LTOdP4=
|
||||
github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo=
|
||||
github.com/compose-spec/compose-go v0.0.0-20200716130117-e87e4f7839e3 h1:+ntlMTrEcScJjlnEOP8P1IIrusJaR93Eazr66YgUueA=
|
||||
github.com/compose-spec/compose-go v0.0.0-20200716130117-e87e4f7839e3/go.mod h1:ArodJ6gsEB7iWKrbV3fSHZ08LlBvSVB0Oqg04fX86t4=
|
||||
github.com/compose-spec/compose-go v0.0.0-20200811091145-837f8f4de457 h1:8ely1LF7H02sIWz6QjgU53YBCiRpYlM9F9u1MeE1ZPk=
|
||||
github.com/compose-spec/compose-go v0.0.0-20200811091145-837f8f4de457/go.mod h1:cS0vAvM6u9yjJgKWIH2yiqYMWO7WGJb+c0Irw+RefqU=
|
||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1 h1:uict5mhHFTzKLUCufdSLym7z/J0CbBJT59lYbP9wtbg=
|
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/console v1.0.0 h1:fU3UuQapBs+zLJu82NhR11Rif1ny2zfMMAyPJzSN5tQ=
|
||||
github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
|
||||
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.3.2 h1:ForxmXkA6tPIvffbrDAcPUIB32QgXkt2XFj+F0UxetA=
|
||||
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
|
@ -76,6 +79,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz
|
|||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
|
@ -137,8 +142,7 @@ github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB
|
|||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
|
@ -157,8 +161,6 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
|||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc=
|
||||
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
|
@ -229,11 +231,11 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
|
|||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mjibson/esc v0.2.0/go.mod h1:9Hw9gxxfHulMF5OJKCyhYD7PzlSdhzXyaGEBRPH1OPs=
|
||||
github.com/moby/term v0.0.0-20200611042045-63b9a826fb74 h1:kvRIeqJNICemq2UFLx8q/Pj+1IRNZS0XPTaMFkuNsvg=
|
||||
github.com/moby/term v0.0.0-20200611042045-63b9a826fb74/go.mod h1:pJ0Ot5YGdTcMdxnPMyGCfAr6fKXe0g9cDlz16MuFEBE=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
|
@ -401,7 +403,9 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -419,8 +423,8 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdO
|
|||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package backend
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/docker/ecs-plugin/pkg/amazon/sdk"
|
||||
"github.com/docker/ecs-plugin/pkg/progress"
|
||||
)
|
||||
|
||||
func NewBackend(profile string, region string) (*Backend, error) {
|
||||
|
@ -17,6 +20,7 @@ func NewBackend(profile string, region string) (*Backend, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Backend{
|
||||
Region: region,
|
||||
api: sdk.NewAPI(sess),
|
||||
|
@ -26,4 +30,9 @@ func NewBackend(profile string, region string) (*Backend, error) {
|
|||
type Backend struct {
|
||||
Region string
|
||||
api sdk.API
|
||||
writer progress.Writer
|
||||
}
|
||||
|
||||
func (b *Backend) SetWriter(context context.Context) {
|
||||
b.writer = progress.ContextWriter(context)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/docker/ecs-plugin/pkg/compose"
|
||||
"github.com/docker/ecs-plugin/pkg/console"
|
||||
)
|
||||
|
||||
func (b *Backend) Down(ctx context.Context, options *cli.ProjectOptions) error {
|
||||
|
@ -18,13 +17,7 @@ func (b *Backend) Down(ctx context.Context, options *cli.ProjectOptions) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := console.NewProgressWriter()
|
||||
err = b.WaitStackCompletion(ctx, name, compose.StackDelete, w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return b.WaitStackCompletion(ctx, name, compose.StackDelete)
|
||||
}
|
||||
|
||||
func (b *Backend) projectName(options *cli.ProjectOptions) (string, error) {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package backend
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
|
@ -13,7 +15,7 @@ import (
|
|||
"github.com/docker/ecs-plugin/pkg/console"
|
||||
)
|
||||
|
||||
func (b *Backend) Logs(ctx context.Context, options *cli.ProjectOptions) error {
|
||||
func (b *Backend) Logs(ctx context.Context, options *cli.ProjectOptions, writer io.Writer) error {
|
||||
name := options.Name
|
||||
if name == "" {
|
||||
project, err := cli.ProjectFromOptions(options)
|
||||
|
@ -26,6 +28,7 @@ func (b *Backend) Logs(ctx context.Context, options *cli.ProjectOptions) error {
|
|||
err := b.api.GetLogs(ctx, name, &logConsumer{
|
||||
colors: map[string]console.ColorFunc{},
|
||||
width: 0,
|
||||
writer: writer,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -45,8 +48,10 @@ func (l *logConsumer) Log(service, container, message string) {
|
|||
l.computeWidth()
|
||||
}
|
||||
prefix := fmt.Sprintf("%-"+strconv.Itoa(l.width)+"s |", service)
|
||||
|
||||
for _, line := range strings.Split(message, "\n") {
|
||||
fmt.Printf("%s %s\n", cf(prefix), line)
|
||||
buf := bytes.NewBufferString(fmt.Sprintf("%s %s\n", cf(prefix), line))
|
||||
l.writer.Write(buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,4 +68,5 @@ func (l *logConsumer) computeWidth() {
|
|||
type logConsumer struct {
|
||||
colors map[string]console.ColorFunc
|
||||
width int
|
||||
writer io.Writer
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/ecs-plugin/pkg/compose"
|
||||
"github.com/docker/ecs-plugin/pkg/console"
|
||||
"github.com/docker/ecs-plugin/pkg/progress"
|
||||
)
|
||||
|
||||
func (b *Backend) Up(ctx context.Context, options *cli.ProjectOptions) error {
|
||||
|
@ -82,10 +83,12 @@ func (b *Backend) Up(ctx context.Context, options *cli.ProjectOptions) error {
|
|||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
w := console.NewProgressWriter()
|
||||
for k := range template.Resources {
|
||||
w.ResourceEvent(k, "PENDING", "")
|
||||
b.writer.Event(progress.Event{
|
||||
ID: k,
|
||||
Status: progress.Working,
|
||||
StatusText: "Pending",
|
||||
})
|
||||
}
|
||||
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
|
@ -96,7 +99,29 @@ func (b *Backend) Up(ctx context.Context, options *cli.ProjectOptions) error {
|
|||
b.Down(ctx, options)
|
||||
}()
|
||||
|
||||
return b.WaitStackCompletion(ctx, project.Name, operation, w)
|
||||
err = b.WaitStackCompletion(ctx, project.Name, operation)
|
||||
// update status for external resources (LB and cluster)
|
||||
loadBalancerName := fmt.Sprintf("%.32s", fmt.Sprintf("%sLoadBalancer", strings.Title(project.Name)))
|
||||
for k := range template.Resources {
|
||||
switch k {
|
||||
case "Cluster":
|
||||
if cluster == "" {
|
||||
continue
|
||||
}
|
||||
case loadBalancerName:
|
||||
if lb == "" {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
b.writer.Event(progress.Event{
|
||||
ID: k,
|
||||
Status: progress.Done,
|
||||
StatusText: "",
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (b Backend) GetVPC(ctx context.Context, project *types.Project) (string, error) {
|
||||
|
|
|
@ -8,10 +8,11 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/docker/ecs-plugin/pkg/console"
|
||||
"github.com/docker/ecs-plugin/pkg/compose"
|
||||
"github.com/docker/ecs-plugin/pkg/progress"
|
||||
)
|
||||
|
||||
func (b *Backend) WaitStackCompletion(ctx context.Context, name string, operation int, w console.ProgressWriter) error {
|
||||
func (b *Backend) WaitStackCompletion(ctx context.Context, name string, operation int) error {
|
||||
knownEvents := map[string]struct{}{}
|
||||
|
||||
// Get the unique Stack ID so we can collect events without getting some from previous deployments with same name
|
||||
|
@ -22,7 +23,6 @@ func (b *Backend) WaitStackCompletion(ctx context.Context, name string, operatio
|
|||
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
done := make(chan bool)
|
||||
|
||||
go func() {
|
||||
b.api.WaitStackComplete(ctx, stackID, operation) //nolint:errcheck
|
||||
ticker.Stop()
|
||||
|
@ -55,11 +55,38 @@ func (b *Backend) WaitStackCompletion(ctx context.Context, name string, operatio
|
|||
resource := aws.StringValue(event.LogicalResourceId)
|
||||
reason := aws.StringValue(event.ResourceStatusReason)
|
||||
status := aws.StringValue(event.ResourceStatus)
|
||||
w.ResourceEvent(resource, status, reason)
|
||||
if stackErr == nil && strings.HasSuffix(status, "_FAILED") {
|
||||
progressStatus := progress.Working
|
||||
|
||||
switch status {
|
||||
case "CREATE_COMPLETE":
|
||||
if operation == compose.StackCreate {
|
||||
progressStatus = progress.Done
|
||||
|
||||
}
|
||||
case "UPDATE_COMPLETE":
|
||||
if operation == compose.StackUpdate {
|
||||
progressStatus = progress.Done
|
||||
}
|
||||
case "DELETE_COMPLETE":
|
||||
if operation == compose.StackDelete {
|
||||
progressStatus = progress.Done
|
||||
}
|
||||
default:
|
||||
if strings.HasSuffix(status, "_FAILED") {
|
||||
progressStatus = progress.Error
|
||||
if stackErr == nil {
|
||||
operation = compose.StackDelete
|
||||
stackErr = fmt.Errorf(reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
b.writer.Event(progress.Event{
|
||||
ID: resource,
|
||||
Status: progressStatus,
|
||||
StatusText: status,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return stackErr
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package compose
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/awslabs/goformation/v4/cloudformation"
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
|
@ -15,8 +16,8 @@ type API interface {
|
|||
CreateContextData(ctx context.Context, params map[string]string) (contextData interface{}, description string, err error)
|
||||
|
||||
Convert(project *types.Project) (*cloudformation.Template, error)
|
||||
Logs(ctx context.Context, options *cli.ProjectOptions) error
|
||||
Ps(background context.Context, options *cli.ProjectOptions) ([]ServiceStatus, error)
|
||||
Logs(ctx context.Context, options *cli.ProjectOptions, writer io.Writer) error
|
||||
Ps(ctx context.Context, options *cli.ProjectOptions) ([]ServiceStatus, error)
|
||||
|
||||
CreateSecret(ctx context.Context, secret Secret) (string, error)
|
||||
InspectSecret(ctx context.Context, id string) (Secret, error)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
@ -24,6 +25,14 @@ var Monochrome = func(s string) string {
|
|||
return s
|
||||
}
|
||||
|
||||
func ansiColor(code, s string) string {
|
||||
return fmt.Sprintf("%s%s%s", ansi(code), s, ansi("0"))
|
||||
}
|
||||
|
||||
func ansi(code string) string {
|
||||
return fmt.Sprintf("\033[%sm", code)
|
||||
}
|
||||
|
||||
func makeColorFunc(code string) ColorFunc {
|
||||
return func(s string) string {
|
||||
return ansiColor(code, s)
|
||||
|
|
|
@ -1,132 +0,0 @@
|
|||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type resource struct {
|
||||
name string
|
||||
status string
|
||||
details string
|
||||
}
|
||||
|
||||
type progress struct {
|
||||
console console
|
||||
resources []*resource
|
||||
}
|
||||
|
||||
type ProgressWriter interface {
|
||||
ResourceEvent(name string, status string, details string)
|
||||
}
|
||||
|
||||
func NewProgressWriter() ProgressWriter {
|
||||
return &progress{
|
||||
console: ansiConsole{os.Stdout},
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
blue = "36;2"
|
||||
red = "31;1"
|
||||
green = "32;1"
|
||||
)
|
||||
|
||||
func (p *progress) ResourceEvent(name string, status string, details string) {
|
||||
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||||
logrus.Debugf("> %s : %s %s\n", name, status, details)
|
||||
return
|
||||
}
|
||||
p.console.MoveUp(len(p.resources))
|
||||
|
||||
newResource := true
|
||||
for _, r := range p.resources {
|
||||
if r.name == name {
|
||||
newResource = false
|
||||
r.status = status
|
||||
r.details = details
|
||||
break
|
||||
}
|
||||
}
|
||||
if newResource {
|
||||
p.resources = append(p.resources, &resource{name, status, details})
|
||||
}
|
||||
|
||||
var width int
|
||||
for _, r := range p.resources {
|
||||
l := len(r.name)
|
||||
if width < l {
|
||||
width = l
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range p.resources {
|
||||
s := r.status
|
||||
if strings.HasSuffix(s, "_IN_PROGRESS") {
|
||||
s = p.console.WiP(s)
|
||||
} else if strings.HasSuffix(s, "_COMPLETE") {
|
||||
s = p.console.OK(s)
|
||||
} else if strings.HasSuffix(s, "_FAILED") {
|
||||
s = p.console.KO(s)
|
||||
}
|
||||
p.console.ClearLine()
|
||||
p.console.Printf("%-"+strconv.Itoa(width)+"s ... %s %s", r.name, s, r.details) // nolint:errcheck
|
||||
p.console.MoveDown(1)
|
||||
}
|
||||
}
|
||||
|
||||
type console interface {
|
||||
Printf(format string, a ...interface{})
|
||||
MoveUp(int)
|
||||
MoveDown(int)
|
||||
ClearLine()
|
||||
OK(string) string
|
||||
KO(string) string
|
||||
WiP(string) string
|
||||
}
|
||||
|
||||
type ansiConsole struct {
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (c ansiConsole) Printf(format string, a ...interface{}) {
|
||||
fmt.Fprintf(c.out, format, a...) // nolint:errcheck
|
||||
fmt.Fprintf(c.out, "\r")
|
||||
}
|
||||
|
||||
func (c ansiConsole) MoveUp(i int) {
|
||||
fmt.Fprintf(c.out, "\033[%dA", i) // nolint:errcheck
|
||||
}
|
||||
|
||||
func (c ansiConsole) MoveDown(i int) {
|
||||
fmt.Fprintf(c.out, "\033[%dB", i) // nolint:errcheck
|
||||
}
|
||||
|
||||
func (c ansiConsole) ClearLine() {
|
||||
fmt.Fprint(c.out, "\033[2K\r") // nolint:errcheck
|
||||
}
|
||||
|
||||
func (c ansiConsole) OK(s string) string {
|
||||
return ansiColor(green, s)
|
||||
}
|
||||
|
||||
func (c ansiConsole) KO(s string) string {
|
||||
return ansiColor(red, s)
|
||||
}
|
||||
|
||||
func (c ansiConsole) WiP(s string) string {
|
||||
return ansiColor(blue, s)
|
||||
}
|
||||
|
||||
func ansiColor(code, s string) string {
|
||||
return fmt.Sprintf("%s%s%s", ansi(code), s, ansi("0"))
|
||||
}
|
||||
|
||||
func ansi(code string) string {
|
||||
return fmt.Sprintf("\033[%sm", code)
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestProgressWriter(t *testing.T) {
|
||||
c := &bufferConsole{}
|
||||
p := progress{
|
||||
console: c,
|
||||
}
|
||||
p.ResourceEvent("resource1", "CREATE_IN_PROGRESS", "")
|
||||
assert.Equal(t, c.lines[0], "resource1 ... CREATE_IN_PROGRESS ")
|
||||
|
||||
p.ResourceEvent("resource2_long_name", "CREATE_IN_PROGRESS", "ok")
|
||||
assert.Equal(t, c.lines[0], "resource1 ... CREATE_IN_PROGRESS ")
|
||||
assert.Equal(t, c.lines[1], "resource2_long_name ... CREATE_IN_PROGRESS ok")
|
||||
|
||||
p.ResourceEvent("resource2_long_name", "CREATE_COMPLETE", "done")
|
||||
assert.Equal(t, c.lines[0], "resource1 ... CREATE_IN_PROGRESS ")
|
||||
assert.Equal(t, c.lines[1], "resource2_long_name ... CREATE_COMPLETE done")
|
||||
|
||||
p.ResourceEvent("resource1", "CREATE_FAILED", "oups")
|
||||
assert.Equal(t, c.lines[0], "resource1 ... CREATE_FAILED oups")
|
||||
assert.Equal(t, c.lines[1], "resource2_long_name ... CREATE_COMPLETE done")
|
||||
}
|
||||
|
||||
type bufferConsole struct {
|
||||
pos int
|
||||
lines []string
|
||||
}
|
||||
|
||||
func (b *bufferConsole) Printf(format string, a ...interface{}) {
|
||||
b.lines[b.pos] = fmt.Sprintf(format, a...)
|
||||
}
|
||||
|
||||
func (b *bufferConsole) MoveUp(i int) {
|
||||
b.pos -= i
|
||||
}
|
||||
|
||||
func (b *bufferConsole) MoveDown(i int) {
|
||||
b.pos += i
|
||||
}
|
||||
|
||||
func (b *bufferConsole) ClearLine() {
|
||||
if len(b.lines) <= b.pos {
|
||||
b.lines = append(b.lines, "")
|
||||
}
|
||||
b.lines[b.pos] = ""
|
||||
}
|
||||
|
||||
func (b *bufferConsole) OK(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
func (b *bufferConsole) KO(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
func (b *bufferConsole) WiP(s string) string {
|
||||
return s
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package progress
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type plainWriter struct {
|
||||
out io.Writer
|
||||
done chan bool
|
||||
}
|
||||
|
||||
func (p *plainWriter) Start(ctx context.Context) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-p.done:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *plainWriter) Event(e Event) {
|
||||
fmt.Println(e.ID, e.Text, e.StatusText)
|
||||
}
|
||||
|
||||
func (p *plainWriter) Stop() {
|
||||
p.done <- true
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package progress
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
type spinner struct {
|
||||
time time.Time
|
||||
index int
|
||||
chars []string
|
||||
stop bool
|
||||
done string
|
||||
}
|
||||
|
||||
func newSpinner() *spinner {
|
||||
chars := []string{
|
||||
"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏",
|
||||
}
|
||||
done := "⠿"
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
chars = []string{"-"}
|
||||
done = "-"
|
||||
}
|
||||
|
||||
return &spinner{
|
||||
index: 0,
|
||||
time: time.Now(),
|
||||
chars: chars,
|
||||
done: done,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spinner) String() string {
|
||||
if s.stop {
|
||||
return s.done
|
||||
}
|
||||
|
||||
d := time.Since(s.time)
|
||||
if d.Milliseconds() > 100 {
|
||||
s.index = (s.index + 1) % len(s.chars)
|
||||
}
|
||||
|
||||
return s.chars[s.index]
|
||||
}
|
||||
|
||||
func (s *spinner) Stop() {
|
||||
s.stop = true
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
package progress
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/buger/goterm"
|
||||
"github.com/morikuni/aec"
|
||||
)
|
||||
|
||||
type ttyWriter struct {
|
||||
out io.Writer
|
||||
events map[string]Event
|
||||
eventIDs []string
|
||||
repeated bool
|
||||
numLines int
|
||||
done chan bool
|
||||
mtx *sync.RWMutex
|
||||
}
|
||||
|
||||
func (w *ttyWriter) Start(ctx context.Context) error {
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
w.print()
|
||||
return ctx.Err()
|
||||
case <-w.done:
|
||||
w.print()
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
w.print()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *ttyWriter) Stop() {
|
||||
w.done <- true
|
||||
}
|
||||
|
||||
func (w *ttyWriter) Event(e Event) {
|
||||
w.mtx.Lock()
|
||||
defer w.mtx.Unlock()
|
||||
if !StringContains(w.eventIDs, e.ID) {
|
||||
w.eventIDs = append(w.eventIDs, e.ID)
|
||||
}
|
||||
if _, ok := w.events[e.ID]; ok {
|
||||
last := w.events[e.ID]
|
||||
switch e.Status {
|
||||
case Done, Error:
|
||||
if last.Status != e.Status {
|
||||
last.stop()
|
||||
}
|
||||
}
|
||||
last.Status = e.Status
|
||||
last.Text = e.Text
|
||||
last.StatusText = e.StatusText
|
||||
w.events[e.ID] = last
|
||||
} else {
|
||||
e.startTime = time.Now()
|
||||
e.spinner = newSpinner()
|
||||
w.events[e.ID] = e
|
||||
}
|
||||
}
|
||||
|
||||
func (w *ttyWriter) print() {
|
||||
w.mtx.Lock()
|
||||
defer w.mtx.Unlock()
|
||||
if len(w.eventIDs) == 0 {
|
||||
return
|
||||
}
|
||||
terminalWidth := goterm.Width()
|
||||
b := aec.EmptyBuilder
|
||||
for i := 0; i <= w.numLines; i++ {
|
||||
b = b.Up(1)
|
||||
}
|
||||
if !w.repeated {
|
||||
b = b.Down(1)
|
||||
}
|
||||
w.repeated = true
|
||||
fmt.Fprint(w.out, b.Column(0).ANSI)
|
||||
|
||||
// Hide the cursor while we are printing
|
||||
fmt.Fprint(w.out, aec.Hide)
|
||||
defer fmt.Fprint(w.out, aec.Show)
|
||||
|
||||
firstLine := fmt.Sprintf("[+] Running %d/%d", numDone(w.events), w.numLines)
|
||||
if w.numLines != 0 && numDone(w.events) == w.numLines {
|
||||
firstLine = aec.Apply(firstLine, aec.BlueF)
|
||||
}
|
||||
fmt.Fprintln(w.out, firstLine)
|
||||
|
||||
var statusPadding int
|
||||
for _, v := range w.eventIDs {
|
||||
l := len(fmt.Sprintf("%s %s", w.events[v].ID, w.events[v].Text))
|
||||
if statusPadding < l {
|
||||
statusPadding = l
|
||||
}
|
||||
}
|
||||
|
||||
numLines := 0
|
||||
for _, v := range w.eventIDs {
|
||||
line := lineText(w.events[v], terminalWidth, statusPadding, runtime.GOOS != "windows")
|
||||
// nolint: errcheck
|
||||
fmt.Fprint(w.out, line)
|
||||
numLines++
|
||||
}
|
||||
|
||||
w.numLines = numLines
|
||||
}
|
||||
|
||||
func lineText(event Event, terminalWidth, statusPadding int, color bool) string {
|
||||
endTime := time.Now()
|
||||
if event.Status != Working {
|
||||
endTime = event.endTime
|
||||
}
|
||||
|
||||
elapsed := endTime.Sub(event.startTime).Seconds()
|
||||
|
||||
textLen := len(fmt.Sprintf("%s %s", event.ID, event.Text))
|
||||
padding := statusPadding - textLen
|
||||
if padding < 0 {
|
||||
padding = 0
|
||||
}
|
||||
text := fmt.Sprintf(" %s %s %s%s %s",
|
||||
event.spinner.String(),
|
||||
event.ID,
|
||||
event.Text,
|
||||
strings.Repeat(" ", padding),
|
||||
event.StatusText,
|
||||
)
|
||||
timer := fmt.Sprintf("%.1fs\n", elapsed)
|
||||
o := align(text, timer, terminalWidth)
|
||||
|
||||
if color {
|
||||
color := aec.WhiteF
|
||||
if event.Status == Done {
|
||||
color = aec.BlueF
|
||||
}
|
||||
if event.Status == Error {
|
||||
color = aec.RedF
|
||||
}
|
||||
return aec.Apply(o, color)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func numDone(events map[string]Event) int {
|
||||
i := 0
|
||||
for _, e := range events {
|
||||
if e.Status == Done {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func align(l, r string, w int) string {
|
||||
return fmt.Sprintf("%-[2]*[1]s %[3]s", l, w-len(r)-1, r)
|
||||
}
|
||||
|
||||
// StringContains check if an array contains a specific value
|
||||
func StringContains(array []string, needle string) bool {
|
||||
for _, val := range array {
|
||||
if val == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package progress
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/moby/term"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// EventStatus indicates the status of an action
|
||||
type EventStatus int
|
||||
|
||||
const (
|
||||
// Working means that the current task is working
|
||||
Working EventStatus = iota
|
||||
// Done means that the current task is done
|
||||
Done
|
||||
// Error means that the current task has errored
|
||||
Error
|
||||
)
|
||||
|
||||
// Event reprensents a progress event
|
||||
type Event struct {
|
||||
ID string
|
||||
Text string
|
||||
Status EventStatus
|
||||
StatusText string
|
||||
Done bool
|
||||
|
||||
startTime time.Time
|
||||
endTime time.Time
|
||||
spinner *spinner
|
||||
}
|
||||
|
||||
func (e *Event) stop() {
|
||||
e.endTime = time.Now()
|
||||
e.spinner.Stop()
|
||||
}
|
||||
|
||||
// Writer can write multiple progress events
|
||||
type Writer interface {
|
||||
Start(context.Context) error
|
||||
Stop()
|
||||
Event(Event)
|
||||
}
|
||||
|
||||
type writerKey struct{}
|
||||
|
||||
// WithContextWriter adds the writer to the context
|
||||
func WithContextWriter(ctx context.Context, writer Writer) context.Context {
|
||||
return context.WithValue(ctx, writerKey{}, writer)
|
||||
}
|
||||
|
||||
// ContextWriter returns the writer from the context
|
||||
func ContextWriter(ctx context.Context) Writer {
|
||||
s, _ := ctx.Value(writerKey{}).(Writer)
|
||||
return s
|
||||
}
|
||||
|
||||
type progressFunc func(context.Context) error
|
||||
|
||||
// Run will run a writer and the progress function
|
||||
// in parallel
|
||||
func Run(ctx context.Context, pf progressFunc) error {
|
||||
eg, _ := errgroup.WithContext(ctx)
|
||||
w, err := NewWriter(os.Stderr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
eg.Go(func() error {
|
||||
return w.Start(context.Background())
|
||||
})
|
||||
|
||||
ctx = WithContextWriter(ctx, w)
|
||||
|
||||
eg.Go(func() error {
|
||||
defer w.Stop()
|
||||
return pf(ctx)
|
||||
})
|
||||
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
// NewWriter returns a new multi-progress writer
|
||||
func NewWriter(out console.File) (Writer, error) {
|
||||
_, isTerminal := term.GetFdInfo(out)
|
||||
|
||||
if isTerminal {
|
||||
con, err := console.ConsoleFromFile(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ttyWriter{
|
||||
out: con,
|
||||
eventIDs: []string{},
|
||||
events: map[string]Event{},
|
||||
repeated: false,
|
||||
done: make(chan bool),
|
||||
mtx: &sync.RWMutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &plainWriter{
|
||||
out: out,
|
||||
done: make(chan bool),
|
||||
}, nil
|
||||
}
|
Loading…
Reference in New Issue