From 246dfb3c0e45d2963501b378353745ab05838844 Mon Sep 17 00:00:00 2001 From: Eoin McAfee <83226740+eoinmcafee00@users.noreply.github.com> Date: Thu, 13 Jan 2022 12:14:53 +0000 Subject: [PATCH] (feat) publish docker data to create drone card (#347) * plugin logic to write card data to publish drone card --- card.go | 63 +++++++++++++++++++++ cmd/drone-docker/main.go | 6 ++ docker.go | 58 +++++++++++++++---- docs/card.json | 118 +++++++++++++++++++++++++++++++++++++++ docs/index.json | 0 go.mod | 2 + go.sum | 7 +++ 7 files changed, 244 insertions(+), 10 deletions(-) create mode 100644 card.go create mode 100644 docs/card.json create mode 100644 docs/index.json diff --git a/card.go b/card.go new file mode 100644 index 0000000..e9bd76a --- /dev/null +++ b/card.go @@ -0,0 +1,63 @@ +package docker + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "time" + + "github.com/drone/drone-go/drone" + + "github.com/inhies/go-bytesize" +) + +func (p Plugin) writeCard() error { + cmd := exec.Command("docker", "inspect", p.Build.Name) + data, err := cmd.CombinedOutput() + if err != nil { + return err + } + + out := Inspect{} + if err := json.Unmarshal(data, &out); err != nil { + return err + } + + inspect := out[0] + inspect.SizeString = fmt.Sprint(bytesize.New(float64(inspect.Size))) + inspect.VirtualSizeString = fmt.Sprint(bytesize.New(float64(inspect.VirtualSize))) + inspect.Time = fmt.Sprint(inspect.Metadata.LastTagTime.Format(time.RFC3339)) + cardData, _ := json.Marshal(inspect) + + card := drone.CardInput{ + Schema: "https://drone-plugins.github.io/drone-docker/card.json", + Data: cardData, + } + + writeCard(p.CardPath, &card) + return nil +} + +func writeCard(path string, card interface{}) { + data, _ := json.Marshal(card) + switch { + case path == "/dev/stdout": + writeCardTo(os.Stdout, data) + case path == "/dev/stderr": + writeCardTo(os.Stderr, data) + case path != "": + ioutil.WriteFile(path, data, 0644) + } +} + +func writeCardTo(out io.Writer, data []byte) { + encoded := base64.StdEncoding.EncodeToString(data) + io.WriteString(out, "\u001B]1338;") + io.WriteString(out, encoded) + io.WriteString(out, "\u001B]0m") + io.WriteString(out, "\n") +} diff --git a/cmd/drone-docker/main.go b/cmd/drone-docker/main.go index b76cd6b..ce652fe 100644 --- a/cmd/drone-docker/main.go +++ b/cmd/drone-docker/main.go @@ -249,6 +249,11 @@ func main() { Usage: "additional host:IP mapping", EnvVar: "PLUGIN_ADD_HOST", }, + cli.StringFlag{ + Name: "drone-card-path", + Usage: "card path location to write to", + EnvVar: "DRONE_CARD_PATH", + }, } if err := app.Run(os.Args); err != nil { @@ -267,6 +272,7 @@ func run(c *cli.Context) error { Email: c.String("docker.email"), Config: c.String("docker.config"), }, + CardPath: c.String("drone-card-path"), Build: docker.Build{ Remote: c.String("remote.url"), Name: c.String("commit.sha"), diff --git a/docker.go b/docker.go index 5de6d58..d80bdf6 100644 --- a/docker.go +++ b/docker.go @@ -64,11 +64,34 @@ type ( // Plugin defines the Docker plugin parameters. Plugin struct { - Login Login // Docker login configuration - Build Build // Docker build configuration - Daemon Daemon // Docker daemon configuration - Dryrun bool // Docker push is skipped - Cleanup bool // Docker purge is enabled + Login Login // Docker login configuration + Build Build // Docker build configuration + Daemon Daemon // Docker daemon configuration + Dryrun bool // Docker push is skipped + Cleanup bool // Docker purge is enabled + CardPath string // Card path to write file to + } + + Inspect []struct { + ID string `json:"Id"` + RepoTags []string `json:"RepoTags"` + RepoDigests []interface{} `json:"RepoDigests"` + Parent string `json:"Parent"` + Comment string `json:"Comment"` + Created time.Time `json:"Created"` + Container string `json:"Container"` + DockerVersion string `json:"DockerVersion"` + Author string `json:"Author"` + Architecture string `json:"Architecture"` + Os string `json:"Os"` + Size int `json:"Size"` + VirtualSize int `json:"VirtualSize"` + Metadata struct { + LastTagTime time.Time `json:"LastTagTime"` + } `json:"Metadata"` + SizeString string + VirtualSizeString string + Time string } ) @@ -157,11 +180,6 @@ func (p Plugin) Exec() error { } } - if p.Cleanup { - cmds = append(cmds, commandRmi(p.Build.Name)) // docker rmi - cmds = append(cmds, commandPrune()) // docker system prune -f - } - // execute all commands in batch mode. for _, cmd := range cmds { cmd.Stdout = os.Stdout @@ -180,6 +198,26 @@ func (p Plugin) Exec() error { } } + // output the adaptive card + if err := p.writeCard(); err != nil { + fmt.Printf("Could not create adaptive card. %s\n", err) + } + + // execute cleanup routines in batch mode + if p.Cleanup { + // clear the slice + cmds = nil + + cmds = append(cmds, commandRmi(p.Build.Name)) // docker rmi + cmds = append(cmds, commandPrune()) // docker system prune -f + + for _, cmd := range cmds { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + trace(cmd) + } + } + return nil } diff --git a/docs/card.json b/docs/card.json new file mode 100644 index 0000000..de02706 --- /dev/null +++ b/docs/card.json @@ -0,0 +1,118 @@ +{ + "type": "AdaptiveCard", + "body": [ + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "items": [ + { + "type": "Image", + "url": "https://d36jcksde1wxzq.cloudfront.net/be7833db9bddb4494d2a7c3dd659199a.png", + "size": "Medium" + } + ], + "width": "auto" + }, + { + "type": "Column", + "items": [ + { + "type": "TextBlock", + "text": "${RepoTags[0]}", + "size": "Medium" + }, + { + "type": "TextBlock", + "text": "DIGEST: ${RepoDigests[0]}", + "wrap": true, + "size": "Small", + "weight": "Lighter", + "isSubtle": true, + "spacing": "Small" + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "items": [ + { + "type": "TextBlock", + "weight": "Lighter", + "text": "OS/ARCH", + "wrap": true, + "size": "Small", + "isSubtle": true, + "spacing": "Medium" + }, + { + "type": "TextBlock", + "text": "${OS}/${Architecture}", + "wrap": true, + "size": "Small", + "spacing": "Small" + } + ], + "separator": true, + "width": "auto" + }, + { + "type": "Column", + "items": [ + { + "type": "TextBlock", + "weight": "Lighter", + "text": "SIZE", + "wrap": true, + "size": "Small", + "isSubtle": true + }, + { + "type": "TextBlock", + "spacing": "Small", + "text": "${SizeString}", + "wrap": true, + "size": "Small" + } + ], + "width": "auto", + "separator": true, + "spacing": "Medium" + }, + { + "type": "Column", + "items": [ + { + "type": "TextBlock", + "weight": "Lighter", + "text": "LAST PUSHED", + "wrap": true, + "size": "Small", + "isSubtle": true + }, + { + "type": "TextBlock", + "spacing": "Small", + "text": "{{DATE(${Time})}} - {{TIME(${Time})}}", + "wrap": true, + "size": "Small" + } + ], + "width": "auto", + "separator": true, + "spacing": "Medium" + } + ], + "style": "default" + } + ], + "width": "stretch" + } + ] + } + ], + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "version": "1.0" +} \ No newline at end of file diff --git a/docs/index.json b/docs/index.json new file mode 100644 index 0000000..e69de29 diff --git a/go.mod b/go.mod index b040264..4ab477e 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/drone-plugins/drone-docker require ( github.com/aws/aws-sdk-go v1.26.7 github.com/coreos/go-semver v0.3.0 + github.com/drone/drone-go v1.7.1 + github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 github.com/joho/godotenv v1.3.0 github.com/sirupsen/logrus v1.3.0 github.com/urfave/cli v1.22.2 diff --git a/go.sum b/go.sum index 2e8569c..53c1733 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/aws/aws-sdk-go v1.26.7 h1:ObjEnmzvSdYy8KVd3me7v/UMyCn81inLy2SyoIPoBkg= github.com/aws/aws-sdk-go v1.26.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -7,6 +8,12 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSY github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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/drone/drone-go v1.7.1 h1:ZX+3Rs8YHUSUQ5mkuMLmm1zr1ttiiE2YGNxF3AnyDKw= +github.com/drone/drone-go v1.7.1/go.mod h1:fxCf9jAnXDZV1yDr0ckTuWd1intvcQwfJmTRpTZ1mXg= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 h1:X3Xxno5Ji8idrNiUoFc7QyXpqhSYlDRYQmc7mlpMBzU= +github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743/go.mod h1:KrtyD5PFj++GKkFS/7/RRrfnRhAMGQwy75GLCHWrCNs= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=