feat: first pass at registry plugin
This commit is contained in:
9
plugins/registry/.gitignore
vendored
Normal file
9
plugins/registry/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/commands
|
||||
/core-post-deploy
|
||||
/subcommands/*
|
||||
/triggers/*
|
||||
/triggers
|
||||
/network-*
|
||||
/install
|
||||
/post-*
|
||||
/report
|
||||
6
plugins/registry/Makefile
Normal file
6
plugins/registry/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
SUBCOMMANDS = subcommands/login subcommands/report subcommands/set
|
||||
TRIGGERS = triggers/install triggers/post-delete triggers/report
|
||||
BUILD = commands subcommands triggers
|
||||
PLUGIN_NAME = registry
|
||||
|
||||
include ../../common.mk
|
||||
12
plugins/registry/go.mod
Normal file
12
plugins/registry/go.mod
Normal file
@@ -0,0 +1,12 @@
|
||||
module github.com/dokku/dokku/plugins/registry
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0
|
||||
github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27
|
||||
github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
)
|
||||
|
||||
replace github.com/dokku/dokku/plugins/common => ../common
|
||||
10
plugins/registry/go.sum
Normal file
10
plugins/registry/go.sum
Normal file
@@ -0,0 +1,10 @@
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
||||
github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 h1:HHUr4P/aKh4quafGxDT9LDasjGdlGkzLbfmmrlng3kA=
|
||||
github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE=
|
||||
github.com/ryanuber/columnize v1.1.2-0.20190319233515-9e6335e58db3 h1:utdYOikI1XjNtTFGCwSM6OmFJblU4ld4gACoJsbadJg=
|
||||
github.com/ryanuber/columnize v1.1.2-0.20190319233515-9e6335e58db3/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
4
plugins/registry/plugin.toml
Normal file
4
plugins/registry/plugin.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core registry plugin"
|
||||
version = "0.24.0"
|
||||
[plugin.config]
|
||||
20
plugins/registry/registry.go
Normal file
20
plugins/registry/registry.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package registry
|
||||
|
||||
var (
|
||||
// DefaultProperties is a map of all valid network properties with corresponding default property values
|
||||
DefaultProperties = map[string]string{
|
||||
"create-repository": "false",
|
||||
"disable-delete-warning": "false",
|
||||
"image-repo": "",
|
||||
"push-on-release": "false",
|
||||
"server": "",
|
||||
}
|
||||
|
||||
// GlobalProperties is a map of all valid global network properties
|
||||
GlobalProperties = map[string]bool{
|
||||
"create-repository": true,
|
||||
"disable-delete-warning": true,
|
||||
"push-on-release": true,
|
||||
"server": true,
|
||||
}
|
||||
)
|
||||
131
plugins/registry/report.go
Normal file
131
plugins/registry/report.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
)
|
||||
|
||||
// ReportSingleApp is an internal function that displays the registry report for one or more apps
|
||||
func ReportSingleApp(appName string, format string, infoFlag string) error {
|
||||
if err := common.VerifyAppName(appName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flags := map[string]common.ReportFunc{
|
||||
"--registry-computed-create-repository": reportComputedCreateRepository,
|
||||
"--registry-global-create-repository": reportGlobalCreateRepository,
|
||||
"--registry-create-repository": reportCreateRepository,
|
||||
"--registry-computed-disable-delete-warning": reportComputedDisableDeleteWarning,
|
||||
"--registry-global-disable-delete-warning": reportGlobalDisableDeleteWarning,
|
||||
"--registry-disable-delete-warning": reportDisableDeleteWarning,
|
||||
"--registry-image-repo": reportImageRepo,
|
||||
"--registry-computed-push-on-release": reportComputedPushOnRelease,
|
||||
"--registry-global-push-on-release": reportGlobalPushOnRelease,
|
||||
"--registry-push-on-release": reportPushOnRelease,
|
||||
"--registry-computed-server": reportComputedServer,
|
||||
"--registry-global-server": reportGlobalServer,
|
||||
"--registry-server": reportServer,
|
||||
"--registry-tag-version": reportTagVersion,
|
||||
}
|
||||
|
||||
flagKeys := []string{}
|
||||
for flagKey := range flags {
|
||||
flagKeys = append(flagKeys, flagKey)
|
||||
}
|
||||
|
||||
trimPrefix := false
|
||||
uppercaseFirstCharacter := true
|
||||
infoFlags := common.CollectReport(appName, infoFlag, flags)
|
||||
return common.ReportSingleApp("registry", appName, infoFlag, infoFlags, flagKeys, format, trimPrefix, uppercaseFirstCharacter)
|
||||
}
|
||||
|
||||
func reportComputedCreateRepository(appName string) string {
|
||||
value := reportCreateRepository(appName)
|
||||
if value == "" {
|
||||
value = reportGlobalCreateRepository(appName)
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
value = DefaultProperties["create-repository"]
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func reportGlobalCreateRepository(appName string) string {
|
||||
return common.PropertyGet("registry", "--global", "create-repository")
|
||||
}
|
||||
|
||||
func reportCreateRepository(appName string) string {
|
||||
return common.PropertyGet("registry", appName, "create-repository")
|
||||
}
|
||||
|
||||
func reportComputedDisableDeleteWarning(appName string) string {
|
||||
value := reportDisableDeleteWarning(appName)
|
||||
if value == "" {
|
||||
value = reportGlobalDisableDeleteWarning(appName)
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
value = DefaultProperties["disable-delete-warning"]
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func reportGlobalDisableDeleteWarning(appName string) string {
|
||||
return common.PropertyGet("registry", "--global", "disable-delete-warning")
|
||||
}
|
||||
|
||||
func reportDisableDeleteWarning(appName string) string {
|
||||
return common.PropertyGet("registry", appName, "disable-delete-warning")
|
||||
}
|
||||
|
||||
func reportImageRepo(appName string) string {
|
||||
return common.PropertyGet("registry", appName, "image-repo")
|
||||
}
|
||||
|
||||
func reportComputedPushOnRelease(appName string) string {
|
||||
value := reportPushOnRelease(appName)
|
||||
if value == "" {
|
||||
value = reportGlobalPushOnRelease(appName)
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
value = DefaultProperties["push-on-release"]
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func reportGlobalPushOnRelease(appName string) string {
|
||||
return common.PropertyGet("registry", "--global", "push-on-release")
|
||||
}
|
||||
|
||||
func reportPushOnRelease(appName string) string {
|
||||
return common.PropertyGet("registry", appName, "push-on-release")
|
||||
}
|
||||
|
||||
func reportComputedServer(appName string) string {
|
||||
value := reportServer(appName)
|
||||
if value == "" {
|
||||
value = reportGlobalServer(appName)
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
value = DefaultProperties["server"]
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func reportGlobalServer(appName string) string {
|
||||
return common.PropertyGet("registry", "--global", "server")
|
||||
}
|
||||
|
||||
func reportServer(appName string) string {
|
||||
return common.PropertyGet("registry", appName, "server")
|
||||
}
|
||||
|
||||
func reportTagVersion(appName string) string {
|
||||
return common.PropertyGet("registry", appName, "tag-version")
|
||||
}
|
||||
57
plugins/registry/src/commands/commands.go
Normal file
57
plugins/registry/src/commands/commands.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
)
|
||||
|
||||
const (
|
||||
helpHeader = `Usage: dokku registry[:COMMAND]
|
||||
|
||||
Manage registry settings for an app
|
||||
|
||||
Additional commands:`
|
||||
|
||||
helpContent = `
|
||||
registry:login [--password-stdin] <server> <username> [<password>], Login to a docker registry
|
||||
registry:report [<app>] [<flag>], Displays a registry report for one or more apps
|
||||
registry:set <app> <property> (<value>), Set or clear a registry property for an app
|
||||
`
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
cmd := flag.Arg(0)
|
||||
switch cmd {
|
||||
case "registry", "registry:help":
|
||||
usage()
|
||||
case "help":
|
||||
command := common.NewShellCmd(fmt.Sprintf("ps -o command= %d", os.Getppid()))
|
||||
command.ShowOutput = false
|
||||
output, err := command.Output()
|
||||
|
||||
if err == nil && strings.Contains(string(output), "--all") {
|
||||
fmt.Println(helpContent)
|
||||
} else {
|
||||
fmt.Print("\n registry, Manage registry settings for an app\n")
|
||||
}
|
||||
default:
|
||||
dokkuNotImplementExitCode, err := strconv.Atoi(os.Getenv("DOKKU_NOT_IMPLEMENTED_EXIT"))
|
||||
if err != nil {
|
||||
fmt.Println("failed to retrieve DOKKU_NOT_IMPLEMENTED_EXIT environment variable")
|
||||
dokkuNotImplementExitCode = 10
|
||||
}
|
||||
os.Exit(dokkuNotImplementExitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
common.CommandUsage(helpHeader, helpContent)
|
||||
}
|
||||
58
plugins/registry/src/subcommands/subcommands.go
Normal file
58
plugins/registry/src/subcommands/subcommands.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
"github.com/dokku/dokku/plugins/registry"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// main entrypoint to all subcommands
|
||||
func main() {
|
||||
parts := strings.Split(os.Args[0], "/")
|
||||
subcommand := parts[len(parts)-1]
|
||||
|
||||
var err error
|
||||
switch subcommand {
|
||||
case "login":
|
||||
args := flag.NewFlagSet("registry:login", flag.ExitOnError)
|
||||
passwordStdin := args.Bool("password-stdin", false, "--password-stdin: read password from stdin")
|
||||
args.Parse(os.Args[2:])
|
||||
server := args.Arg(0)
|
||||
username := args.Arg(1)
|
||||
password := args.Arg(2)
|
||||
err = registry.CommandLogin(server, username, password, *passwordStdin)
|
||||
case "report":
|
||||
args := flag.NewFlagSet("registry:report", flag.ExitOnError)
|
||||
format := args.String("format", "stdout", "format: [ stdout | json ]")
|
||||
osArgs, infoFlag, flagErr := common.ParseReportArgs("registry", os.Args[2:])
|
||||
if flagErr == nil {
|
||||
args.Parse(osArgs)
|
||||
appName := args.Arg(0)
|
||||
err = registry.CommandReport(appName, *format, infoFlag)
|
||||
}
|
||||
case "set":
|
||||
args := flag.NewFlagSet("registry:set", flag.ExitOnError)
|
||||
global := args.Bool("global", false, "--global: set a global property")
|
||||
args.Parse(os.Args[2:])
|
||||
appName := args.Arg(0)
|
||||
property := args.Arg(1)
|
||||
value := args.Arg(2)
|
||||
if *global {
|
||||
appName = "--global"
|
||||
property = args.Arg(0)
|
||||
value = args.Arg(1)
|
||||
}
|
||||
err = registry.CommandSet(appName, property, value)
|
||||
default:
|
||||
err = fmt.Errorf("Invalid plugin subcommand call: %s", subcommand)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common.LogFailWithError(err)
|
||||
}
|
||||
}
|
||||
36
plugins/registry/src/triggers/triggers.go
Normal file
36
plugins/registry/src/triggers/triggers.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
"github.com/dokku/dokku/plugins/registry"
|
||||
)
|
||||
|
||||
// main entrypoint to all triggers
|
||||
func main() {
|
||||
parts := strings.Split(os.Args[0], "/")
|
||||
trigger := parts[len(parts)-1]
|
||||
flag.Parse()
|
||||
|
||||
var err error
|
||||
switch trigger {
|
||||
case "install":
|
||||
err = registry.TriggerInstall()
|
||||
case "post-delete":
|
||||
appName := flag.Arg(0)
|
||||
err = registry.TriggerPostDelete(appName)
|
||||
case "report":
|
||||
appName := flag.Arg(0)
|
||||
err = registry.ReportSingleApp(appName, "", "")
|
||||
default:
|
||||
err = fmt.Errorf("Invalid plugin trigger call: %s", trigger)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common.LogFailWithError(err)
|
||||
}
|
||||
}
|
||||
65
plugins/registry/subcommands.go
Normal file
65
plugins/registry/subcommands.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
)
|
||||
|
||||
func CommandLogin(server string, username string, password string, passwordStdin bool) error {
|
||||
if passwordStdin {
|
||||
stdin, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
password = strings.TrimSpace(string(stdin))
|
||||
}
|
||||
|
||||
command := []string{
|
||||
common.DockerBin(),
|
||||
"login",
|
||||
"--username",
|
||||
username,
|
||||
"--password-stdin",
|
||||
server,
|
||||
}
|
||||
|
||||
reader, writer := io.Pipe()
|
||||
writer.Write([]byte(password))
|
||||
loginCmd := common.NewShellCmd(strings.Join(command, " "))
|
||||
loginCmd.Command.Stdin = reader
|
||||
if !loginCmd.Execute() {
|
||||
return errors.New("Failed to log into registry")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommandReport displays a registry report for one or more apps
|
||||
func CommandReport(appName string, format string, infoFlag string) error {
|
||||
if len(appName) == 0 {
|
||||
apps, err := common.DokkuApps()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, appName := range apps {
|
||||
if err := ReportSingleApp(appName, format, infoFlag); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return ReportSingleApp(appName, format, infoFlag)
|
||||
}
|
||||
|
||||
// CommandSet set or clear a registry property for an app
|
||||
func CommandSet(appName string, property string, value string) error {
|
||||
common.CommandPropertySet("registry", appName, property, value, DefaultProperties, GlobalProperties)
|
||||
return nil
|
||||
}
|
||||
21
plugins/registry/triggers.go
Normal file
21
plugins/registry/triggers.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
)
|
||||
|
||||
// TriggerInstall runs the install step for the registry plugin
|
||||
func TriggerInstall() error {
|
||||
if err := common.PropertySetup("registry"); err != nil {
|
||||
return fmt.Errorf("Unable to install the registry plugin: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerPostDelete destroys the registry property for a given app container
|
||||
func TriggerPostDelete(appName string) error {
|
||||
return common.PropertyDestroy("registry", appName)
|
||||
}
|
||||
Reference in New Issue
Block a user