diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..57a1cd96b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +target/ +mcs +!mcs/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..338ff00dd --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,48 @@ +name: goreleaser + +on: + pull_request: + push: + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Unshallow + run: git fetch --prune --unshallow + - + name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.14.x + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@53acad1befee355d46f71cccf6ab4d885eb4f77f + with: + version: latest + args: release --skip-publish --rm-dist --snapshot + - + name: Upload Win64 Binaries + uses: actions/upload-artifact@v1 + if: success() + with: + name: MCS-Snapshot-Build-Win64 + path: dist/mcs_windows_amd64 + - + name: Upload Linux Binaries + uses: actions/upload-artifact@v1 + if: success() + with: + name: MCS-Snapshot-Build-Linux-amd64 + path: dist/mcs_linux_amd64 + - + name: Upload MacOS Binaries + uses: actions/upload-artifact@v1 + if: success() + with: + name: MCS-Snapshot-Build-MacOSX-amd64 + path: dist/mcs_darwin_amd64 diff --git a/.gitignore b/.gitignore index b2ae56ba9..976d6f065 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ vendor/ target/ mcs !mcs/ + +dist/ diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 000000000..61e2fb82f --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,75 @@ +# This is an example goreleaser.yaml file with some sane defaults. +# Make sure to check the documentation at http://goreleaser.com +project_name: mcs + +before: + hooks: + # you may remove this if you don't use vgo + - go mod tidy +builds: + - + goos: + - freebsd + - windows + - linux + - darwin + goarch: + - amd64 + - arm64 + env: + - CGO_ENABLED=0 + main: ./cmd/mcs/ + flags: + - -trimpath + - --tags=kqueue + ldflags: + - -s -w -X github.com/minio/mcs/pkg.ReleaseTag={{.Tag}} -X github.com/minio/warp/pkg.CommitID={{.FullCommit}} -X github.com/minio/warp/pkg.Version={{.Version}} -X github.com/minio/warp/pkg.ShortCommitID={{.ShortCommit}} -X github.com/minio/warp/pkg.ReleaseTime={{.Date}} +archives: + - + replacements: + darwin: Darwin + linux: Linux + windows: Windows + freebsd: FreeBSD + amd64: x86_64 + format_overrides: + - goos: windows + format: zip + files: + - README.md + - LICENSE +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: 'snapshot-{{ time "2006-01-02" }}' +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' +nfpms: + - + vendor: MinIO Inc. + homepage: https://github.com/minio/mcs + maintainer: MinIO + description: MinIO Console Server + license: GNU Affero General Public License v3.0 + formats: + - deb + - rpm + replacements: + darwin: Darwin + linux: Linux + freebsd: FreeBSD + amd64: x86_64 +dockers: + - + # GOOS of the built binary that should be used. + goos: linux + # GOARCH of the built binary that should be used. + goarch: amd64 + dockerfile: Dockerfile.release + image_templates: + - "minio/mcs:{{ .Tag }}" + - "minio/mcs:latest" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..4dd45d7aa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM golang:1.14.1 + +ADD go.mod /go/src/github.com/minio/mcs/go.mod +ADD go.sum /go/src/github.com/minio/mcs/go.sum +WORKDIR /go/src/github.com/minio/mcs/ +# Get dependencies - will also be cached if we won't change mod/sum +RUN go mod download + +ADD . /go/src/github.com/minio/mcs/ +WORKDIR /go/src/github.com/minio/mcs/ + +ENV CGO_ENABLED=0 + +RUN apt-get update -y && apt-get install -y ca-certificates +RUN go build -ldflags "-w -s" -a -o mcs ./cmd/mcs + +FROM scratch +MAINTAINER MinIO Development "dev@min.io" +EXPOSE 9090 + +COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=0 /go/src/github.com/minio/mcs/mcs . + +CMD ["/mcs"] diff --git a/Dockerfile.release b/Dockerfile.release new file mode 100644 index 000000000..1b89149d8 --- /dev/null +++ b/Dockerfile.release @@ -0,0 +1,6 @@ +FROM scratch +MAINTAINER MinIO Development "dev@min.io" +EXPOSE 9090 +COPY mcs /mcs + +ENTRYPOINT ["/mcs"] diff --git a/Makefile b/Makefile index 7ea8ed42e..cf90da101 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ default: mcs .PHONY: mcs mcs: @echo "Building mcs binary to './mcs'" - @(CGO_ENABLED=0 go build --tags kqueue --ldflags "-s -w" -o mcs ./cmd/mcs) + @(CGO_ENABLED=0 go build --tags=kqueue --ldflags "-s -w" -o mcs ./cmd/mcs) swagger-gen: @echo "Generating swagger server code from yaml" diff --git a/cmd/mcs/main.go b/cmd/mcs/main.go index 50f447431..1b0dcdc2a 100644 --- a/cmd/mcs/main.go +++ b/cmd/mcs/main.go @@ -18,56 +18,122 @@ package main import ( - "flag" - "log" "os" + "path/filepath" + "sort" - "github.com/go-openapi/loads" - flags "github.com/jessevdk/go-flags" - "github.com/minio/m3/mcs/restapi" - "github.com/minio/m3/mcs/restapi/operations" + "github.com/minio/m3/mcs/pkg" + + "github.com/minio/minio/pkg/console" + "github.com/minio/minio/pkg/trie" + "github.com/minio/minio/pkg/words" + + "github.com/minio/cli" ) -var portFlag = flag.Int("port", 9090, "Port to run this service on") +// Help template for m3. +var mcsHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} -func main() { +DESCRIPTION: + {{.Description}} - swaggerSpec, err := loads.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON) - if err != nil { - log.Fatalln(err) +USAGE: + {{.HelpName}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}}{{end}} [ARGS...] + +COMMANDS: + {{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} + {{end}}{{if .VisibleFlags}} +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}}{{end}} +VERSION: + {{.Version}} +` + +var appCmds = []cli.Command{ + serverCmd, + versionCmd, +} + +func newApp(name string) *cli.App { + // Collection of m3 commands currently supported are. + commands := []cli.Command{} + + // Collection of m3 commands currently supported in a trie tree. + commandsTree := trie.NewTrie() + + // registerCommand registers a cli command. + registerCommand := func(command cli.Command) { + commands = append(commands, command) + commandsTree.Insert(command.Name) } - api := operations.NewMcsAPI(swaggerSpec) - server := restapi.NewServer(api) - defer server.Shutdown() + // register commands + for _, cmd := range appCmds { + registerCommand(cmd) + } - parser := flags.NewParser(server, flags.Default) - parser.ShortDescription = "MinIO Console Server" - parser.LongDescription = swaggerSpec.Spec().Info.Description - server.ConfigureFlags() - for _, optsGroup := range api.CommandLineOptionsGroups { - _, err := parser.AddGroup(optsGroup.ShortDescription, optsGroup.LongDescription, optsGroup.Options) - if err != nil { - log.Fatalln(err) + findClosestCommands := func(command string) []string { + var closestCommands []string + for _, value := range commandsTree.PrefixMatch(command) { + closestCommands = append(closestCommands, value.(string)) } - } - if _, err := parser.Parse(); err != nil { - code := 1 - if fe, ok := err.(*flags.Error); ok { - if fe.Type == flags.ErrHelp { - code = 0 + sort.Strings(closestCommands) + // Suggest other close commands - allow missed, wrongly added and + // even transposed characters + for _, value := range commandsTree.Walk(commandsTree.Root()) { + if sort.SearchStrings(closestCommands, value.(string)) < len(closestCommands) { + continue + } + // 2 is arbitrary and represents the max + // allowed number of typed errors + if words.DamerauLevenshteinDistance(command, value.(string)) < 2 { + closestCommands = append(closestCommands, value.(string)) } } - os.Exit(code) - } - // Parse flags - flag.Parse() - server.ConfigureAPI() - server.Port = *portFlag - if err := server.Serve(); err != nil { - log.Fatalln(err) + return closestCommands } + cli.HelpFlag = cli.BoolFlag{ + Name: "help, h", + Usage: "show help", + } + + app := cli.NewApp() + app.Name = name + app.Version = pkg.Version + app.Author = "MinIO, Inc." + app.Usage = "mcs COMMAND" + app.Description = `MinIO Console Server` + app.Commands = commands + app.HideHelpCommand = true // Hide `help, h` command, we already have `minio --help`. + app.CustomAppHelpTemplate = mcsHelpTemplate + app.CommandNotFound = func(ctx *cli.Context, command string) { + console.Printf("‘%s’ is not a mcs sub-command. See ‘mcs --help’.\n", command) + closestCommands := findClosestCommands(command) + if len(closestCommands) > 0 { + console.Println() + console.Println("Did you mean one of these?") + for _, cmd := range closestCommands { + console.Printf("\t‘%s’\n", cmd) + } + } + + os.Exit(1) + } + + return app +} + +func main() { + args := os.Args + // Set the orchestrator app name. + appName := filepath.Base(args[0]) + // Run the app - exit on error. + if err := newApp(appName).Run(args); err != nil { + os.Exit(1) + } } diff --git a/cmd/mcs/server.go b/cmd/mcs/server.go new file mode 100644 index 000000000..3b01d5e7a --- /dev/null +++ b/cmd/mcs/server.go @@ -0,0 +1,86 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package main + +import ( + "flag" + "log" + "os" + + "github.com/go-openapi/loads" + "github.com/jessevdk/go-flags" + "github.com/minio/cli" + "github.com/minio/m3/mcs/restapi" + "github.com/minio/m3/mcs/restapi/operations" +) + +// starts the server +var serverCmd = cli.Command{ + Name: "server", + Aliases: []string{"srv"}, + Usage: "starts mcs server", + Action: startServer, + Flags: []cli.Flag{ + cli.IntFlag{ + Name: "port", + Value: 9090, + Usage: "Server port", + }, + }, +} + +// starts the controller +func startServer(ctx *cli.Context) error { + swaggerSpec, err := loads.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON) + if err != nil { + log.Fatalln(err) + } + + api := operations.NewMcsAPI(swaggerSpec) + server := restapi.NewServer(api) + defer server.Shutdown() + + parser := flags.NewParser(server, flags.Default) + parser.ShortDescription = "MinIO Console Server" + parser.LongDescription = swaggerSpec.Spec().Info.Description + server.ConfigureFlags() + for _, optsGroup := range api.CommandLineOptionsGroups { + _, err := parser.AddGroup(optsGroup.ShortDescription, optsGroup.LongDescription, optsGroup.Options) + if err != nil { + log.Fatalln(err) + } + } + + if _, err := parser.Parse(); err != nil { + code := 1 + if fe, ok := err.(*flags.Error); ok { + if fe.Type == flags.ErrHelp { + code = 0 + } + } + os.Exit(code) + } + // Parse flags + flag.Parse() + server.ConfigureAPI() + server.Port = ctx.Int("port") + + if err := server.Serve(); err != nil { + log.Fatalln(err) + } + return nil +} diff --git a/cmd/mcs/version.go b/cmd/mcs/version.go new file mode 100644 index 000000000..dbe366552 --- /dev/null +++ b/cmd/mcs/version.go @@ -0,0 +1,37 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package main + +import ( + "fmt" + + "github.com/minio/cli" + "github.com/minio/m3/mcs/pkg" +) + +// starts the server +var versionCmd = cli.Command{ + Name: "version", + Usage: "shows mcs version", + Action: version, +} + +// starts the controller +func version(ctx *cli.Context) error { + fmt.Printf("MCS version %s (%s - %s. Commit %s)", pkg.Version, pkg.ReleaseTag, pkg.ReleaseTime, pkg.CommitID) + return nil +} diff --git a/go.mod b/go.mod index 2ecd76149..4890f00a3 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/go-openapi/swag v0.19.8 github.com/go-openapi/validate v0.19.7 github.com/jessevdk/go-flags v1.4.0 + github.com/minio/cli v1.22.0 github.com/minio/mc v0.0.0-20200403024131-4d36c1f8b856 github.com/minio/minio v0.0.0-20200327214830-6f992134a25f github.com/minio/minio-go/v6 v6.0.51-0.20200401083717-eadbcae2a0e6 diff --git a/pkg/build-constant.go b/pkg/build-constant.go new file mode 100644 index 000000000..077fa3e58 --- /dev/null +++ b/pkg/build-constant.go @@ -0,0 +1,30 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package pkg + +var ( + // Version - the version being released (v prefix stripped) + Version = "(dev)" + // ReleaseTag - the current git tag + ReleaseTag = "(no tag)" + // ReleaseTime - current UTC date in RFC3339 format. + ReleaseTime = "(no release)" + // CommitID - latest commit id. + CommitID = "(dev)" + // ShortCommitID - first 12 characters from CommitID. + ShortCommitID = "(dev)" +)