golang で docker サブコマンドを実装する

docker-compose 当初 Python で書かれていましたが、v2 からは golang で書き直され、使い方も (v1 の docker-compose では無く) docker compose 〜 といった具合に利用するように変更されました。 docker コマンドのサブコマンドとして動作しているように見えますが、これは cobra を使って実装されているようです。 以下は cobra の公式ページからの引用です。

Cobra is both a library for creating powerful modern CLI applications as well as a program to generate applications and command files.

docker サブコマンドとしての実装例は下記が参考になります。

  • https://github.com/docker/cli/blob/master/cli-plugins/examples/helloworld/main.go
  • https://github.com/docker/compose/blob/v2/cmd/main.go

上記を参考に、更にダイエットして「最小の docker サブコマンド」をメモしておきます。

main.go というファイル名で以下を保存します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main

import (
    "fmt"

    "github.com/docker/cli/cli-plugins/manager"
    "github.com/docker/cli/cli-plugins/plugin"
    "github.com/docker/cli/cli/command"
    "github.com/spf13/cobra"
)

func pluginMain() {
    var (
        preRun bool
    )
    plugin.Run(func(dockerCli command.Cli) *cobra.Command {
        cmd := &cobra.Command {
            Use:   "hello",
            Short: "A basic Hello World plugin for tests",
            PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
                if err := plugin.PersistentPreRunE(cmd, args); err != nil {
                    return err
                }
                if preRun {
                    fmt.Fprintf(dockerCli.Err(), "Plugin PersistentPreRunE called")
                }
                return nil
            },
            RunE: func(cmd *cobra.Command, args []string) error {
                fmt.Fprintf(dockerCli.Out(), "Hello, World!!\n")
                return dockerCli.ConfigFile().Save()
            },
        }
        return cmd
    },
        manager.Metadata{
            SchemaVersion: "0.1.0",
            Vendor:        "Docker Inc.",
            Version:       "0.0.1",
        })
}

func main() {
    pluginMain()
}

モジュールを用意し、ビルドします。

1
2
3
go mod init docker-hello
go mod tidy
go build main.go

作成されたバイナリは docker のサブコマンドとしてシステムワイドに利用出来るように /usr/local/lib/docker/cli-plugins/ へコピーします。

1
cp /root/docker-purge/main /usr/local/lib/docker/cli-plugins/docker-hello

これで以下のように docker コマンドのサブコマンドとして実行出来ます。

1
2
# docker hello
Hello, World!!