Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com>pull/926/head
parent
0ddae51ce7
commit
c5e81f8e31
@ -0,0 +1,352 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package color print colors supported by the current terminal.
|
||||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/openim-sigs/component-base/pkg/util/stringutil"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
// ColorOptions is an options struct to support color subcommands.
|
||||
type ColorOptions struct {
|
||||
Type []string
|
||||
Example bool
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
colorLong = templates.LongDesc(`Print the colors supported by the current terminal.
|
||||
|
||||
Color lets you use colorized outputs in terms of ANSI Escape Codes in Go (Golang).
|
||||
It has support for Windows too! The API can be used in several ways, pick one that suits you.
|
||||
|
||||
Find more information at:
|
||||
https://github.com/fatih/color`)
|
||||
|
||||
colorExample = templates.Examples(`
|
||||
# Print supported foreground and background colors
|
||||
iamctl color
|
||||
|
||||
# Print supported colors by type
|
||||
iamctl color -t fg-hi
|
||||
|
||||
# Print all supported colors
|
||||
iamctl color -t all`)
|
||||
|
||||
availableTypes = []string{"fg", "fg-hi", "bg", "bg-hi", "base", "all"}
|
||||
|
||||
colorCodeExample = templates.Examples(`
|
||||
### 1. Standard colors
|
||||
|
||||
// Print with default helper functions
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// A newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// These are using the default foreground colors
|
||||
color.Red("We have red")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
### 2. Mix and reuse colors
|
||||
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with white background.")
|
||||
|
||||
### 3. Use your own output (io.Writer)
|
||||
|
||||
// Use your own io.Writer output
|
||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||
|
||||
blue := color.New(color.FgBlue)
|
||||
blue.Fprint(writer, "This will print text in blue.")
|
||||
|
||||
### 4. Custom print functions (PrintFunc)
|
||||
|
||||
// Create a custom print function for convenience
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("Warning")
|
||||
red("Error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("Don't forget this...")
|
||||
|
||||
### 5. Custom fprint functions (FprintFunc)
|
||||
|
||||
blue := color.New(FgBlue).FprintfFunc()
|
||||
blue(myWriter, "important notice: %s", stars)
|
||||
|
||||
// Mix up with multiple attributes
|
||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||
success(myWriter, "Don't forget this...")
|
||||
|
||||
### 6. Insert into noncolor strings (SprintFunc)
|
||||
|
||||
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
red := color.New(color.FgRed).SprintFunc()
|
||||
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
||||
fmt.Printf("This %s rocks!\n", info("package"))
|
||||
|
||||
// Use helper functions
|
||||
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
|
||||
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
|
||||
|
||||
// Windows supported too! Just don't forget to change the output to color.Output
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
|
||||
### 7. Plug into existing code
|
||||
|
||||
// Use handy standard colors
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will now be in yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // Don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // Use it in your function
|
||||
|
||||
fmt.Println("All text will now be bold magenta.")
|
||||
|
||||
### 8. Disable/Enable color
|
||||
|
||||
There might be a case where you want to explicitly disable/enable color output. the
|
||||
go-isatty package will automatically disable color output for non-tty output streams
|
||||
(for example if the output were piped directly to less)
|
||||
|
||||
Color has support to disable/enable colors both globally and for single color
|
||||
definitions. For example suppose you have a CLI app and a --no-color bool flag. You
|
||||
can easily disable the color output with:
|
||||
|
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")`)
|
||||
)
|
||||
|
||||
// NewColorOptions returns an initialized ColorOptions instance.
|
||||
func NewColorOptions(ioStreams genericclioptions.IOStreams) *ColorOptions {
|
||||
return &ColorOptions{
|
||||
Type: []string{},
|
||||
Example: false,
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdColor returns new initialized instance of color sub command.
|
||||
func NewCmdColor(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewColorOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "color",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Print colors supported by the current terminal",
|
||||
TraverseChildren: true,
|
||||
Long: colorLong,
|
||||
Example: colorExample,
|
||||
// NoArgs, ArbitraryArgs, OnlyValidArgs, MinimumArgs, MaximumArgs, ExactArgs, ExactArgs
|
||||
ValidArgs: []string{},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
cmd.Flags().StringSliceVarP(&o.Type, "type", "t", o.Type,
|
||||
fmt.Sprintf("Specify the color type, available types: `%s`.", strings.Join(availableTypes, ",")))
|
||||
cmd.Flags().BoolVar(&o.Example, "example", o.Example, "Print code to show how to use color package.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *ColorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
if len(o.Type) == 0 {
|
||||
o.Type = []string{"fg", "bg"}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if stringutil.StringIn("all", o.Type) {
|
||||
o.Type = []string{"fg", "fg-hi", "bg", "bg-hi", "base"}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *ColorOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
for _, t := range o.Type {
|
||||
if !stringutil.StringIn(t, availableTypes) {
|
||||
return cmdutil.UsageErrorf(cmd, "--type must be in: %s", strings.Join(availableTypes, ","))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a color subcommand using the specified options.
|
||||
func (o *ColorOptions) Run(args []string) error {
|
||||
if o.Example {
|
||||
fmt.Fprint(o.Out, colorCodeExample+"\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var data [][]string
|
||||
|
||||
// print table to stdout
|
||||
table := tablewriter.NewWriter(o.Out)
|
||||
|
||||
// set table attributes
|
||||
table.SetAutoMergeCells(true)
|
||||
table.SetRowLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetAutoWrapText(false)
|
||||
table.SetAutoFormatHeaders(true)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_CENTER)
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetHeader([]string{"Category", "Color Name", "Effect"})
|
||||
table.SetHeaderColor(tablewriter.Colors{tablewriter.FgGreenColor},
|
||||
tablewriter.Colors{tablewriter.FgRedColor},
|
||||
tablewriter.Colors{tablewriter.FgCyanColor})
|
||||
|
||||
for _, t := range o.Type {
|
||||
switch t {
|
||||
// Foreground text colors
|
||||
case "fg":
|
||||
fg := [][]string{
|
||||
{"fg", "black", color.BlackString("color.BlackString")},
|
||||
{"fg", "red", color.RedString("color.RedString")},
|
||||
{"fg", "green", color.GreenString("color.GreenString")},
|
||||
{"fg", "yellow", color.YellowString("color.YellowString")},
|
||||
{"fg", "blue", color.BlueString("color.BlueString")},
|
||||
{"fg", "magenta", color.MagentaString("color.MagentaString")},
|
||||
{"fg", "Cyan", color.CyanString("color.CyanString")},
|
||||
{"fg", "white", color.WhiteString("color.WhiteString")},
|
||||
}
|
||||
data = append(data, fg...)
|
||||
// Foreground Hi-Intensity text colors
|
||||
case "fg-hi":
|
||||
fg := [][]string{
|
||||
{"fg-hi", "black", color.HiBlackString("color.HiBlackString")},
|
||||
{"fg-hi", "red", color.HiRedString("color.HiRedString")},
|
||||
{"fg-hi", "green", color.HiGreenString("color.HiGreenString")},
|
||||
{"fg-hi", "yellow", color.HiYellowString("color.HiYellowString")},
|
||||
{"fg-hi", "blue", color.HiBlueString("color.HiBlueString")},
|
||||
{"fg-hi", "magenta", color.HiMagentaString("color.HiMagentaString")},
|
||||
{"fg-hi", "Cyan", color.HiCyanString("color.HiCyanString")},
|
||||
{"fg-hi", "white", color.HiWhiteString("color.HiWhiteString")},
|
||||
}
|
||||
data = append(data, fg...)
|
||||
|
||||
// Background text colors
|
||||
case "bg":
|
||||
bg := [][]string{
|
||||
{"bg", "black", color.New(color.FgWhite, color.BgBlack).SprintFunc()("color.BgBlack")},
|
||||
{"bg", "red", color.New(color.FgBlack, color.BgRed).SprintFunc()("color.BgRed")},
|
||||
{"bg", "greep", color.New(color.FgBlack, color.BgGreen).SprintFunc()("color.BgGreen")},
|
||||
{"bg", "yellow", color.New(color.FgWhite, color.BgYellow).SprintFunc()("color.BgYellow")},
|
||||
{"bg", "blue", color.New(color.FgWhite, color.BgBlue).SprintFunc()("color.BgBlue")},
|
||||
{"bg", "magenta", color.New(color.FgWhite, color.BgMagenta).SprintFunc()("color.BgMagenta")},
|
||||
{"bg", "cyan", color.New(color.FgWhite, color.BgCyan).SprintFunc()("color.BgCyan")},
|
||||
{"bg", "white", color.New(color.FgBlack, color.BgWhite).SprintFunc()("color.BgWhite")},
|
||||
}
|
||||
data = append(data, bg...)
|
||||
// Background Hi-Intensity text colors
|
||||
case "bg-hi":
|
||||
bghi := [][]string{
|
||||
{"bg-hi", "black", color.New(color.FgWhite, color.BgHiBlack).SprintFunc()("color.BgHiBlack")},
|
||||
{"bg-hi", "red", color.New(color.FgBlack, color.BgHiRed).SprintFunc()("color.BgHiRed")},
|
||||
{"bg-hi", "greep", color.New(color.FgBlack, color.BgHiGreen).SprintFunc()("color.BgHiGreen")},
|
||||
{"bg-hi", "yellow", color.New(color.FgWhite, color.BgHiYellow).SprintFunc()("color.BgHiYellow")},
|
||||
{"bg-hi", "blue", color.New(color.FgWhite, color.BgHiBlue).SprintFunc()("color.BgHiBlue")},
|
||||
{"bg-hi", "magenta", color.New(color.FgWhite, color.BgHiMagenta).SprintFunc()("color.BgHiMagenta")},
|
||||
{"bg-hi", "cyan", color.New(color.FgWhite, color.BgHiCyan).SprintFunc()("color.BgHiCyan")},
|
||||
{"bg-hi", "white", color.New(color.FgBlack, color.BgHiWhite).SprintFunc()("color.BgHiWhite")},
|
||||
}
|
||||
data = append(data, bghi...)
|
||||
// Base attributes
|
||||
case "base":
|
||||
base := [][]string{
|
||||
{"base", "black", color.New(color.FgGreen, color.Reset).SprintFunc()("color.Reset")},
|
||||
{"base", "red", color.New(color.FgGreen, color.Bold).SprintFunc()("color.Bold")},
|
||||
{"base", "greep", color.New(color.FgGreen, color.Faint).SprintFunc()("color.Faint")},
|
||||
{"base", "yellow", color.New(color.FgGreen, color.Italic).SprintFunc()("color.Italic")},
|
||||
{"base", "blue", color.New(color.FgGreen, color.Underline).SprintFunc()("color.Underline")},
|
||||
{"base", "magenta", color.New(color.FgGreen, color.BlinkSlow).SprintFunc()("color.BlinkSlow")},
|
||||
{"base", "cyan", color.New(color.FgGreen, color.BlinkRapid).SprintFunc()("color.BlinkRapid")},
|
||||
{"base", "white", color.New(color.FgGreen, color.ReverseVideo).SprintFunc()("color.ReverseVideo")},
|
||||
{"base", "white", color.New(color.FgGreen, color.Concealed).SprintFunc()("color.Concealed")},
|
||||
{"base", "white", color.New(color.FgGreen, color.CrossedOut).SprintFunc()("color.CrossedOut")},
|
||||
}
|
||||
data = append(data, base...)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
table.AppendBulk(data)
|
||||
table.Render()
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,283 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package completion output shell completion code for the specified shell (bash or zsh).
|
||||
package completion
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
)
|
||||
|
||||
const defaultBoilerPlate = `
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
`
|
||||
|
||||
var (
|
||||
completionLong = templates.LongDesc(`
|
||||
Output shell completion code for the specified shell (bash or zsh).
|
||||
The shell code must be evaluated to provide interactive
|
||||
completion of iamctl commands. This can be done by sourcing it from
|
||||
the .bash_profile.
|
||||
|
||||
Detailed instructions on how to do this are available here:
|
||||
http://github.com/marmotedu/iam/docs/installation/iamctl.md#enabling-shell-autocompletion
|
||||
|
||||
Note for zsh users: [1] zsh completions are only supported in versions of zsh >= 5.2`)
|
||||
|
||||
completionExample = templates.Examples(`
|
||||
# Installing bash completion on macOS using homebrew
|
||||
## If running Bash 3.2 included with macOS
|
||||
brew install bash-completion
|
||||
## or, if running Bash 4.1+
|
||||
brew install bash-completion@2
|
||||
## If iamctl is installed via homebrew, this should start working immediately.
|
||||
## If you've installed via other means, you may need add the completion to your completion directory
|
||||
iamctl completion bash > $(brew --prefix)/etc/bash_completion.d/iamctl
|
||||
|
||||
|
||||
# Installing bash completion on Linux
|
||||
## If bash-completion is not installed on Linux, please install the 'bash-completion' package
|
||||
## via your distribution's package manager.
|
||||
## Load the iamctl completion code for bash into the current shell
|
||||
source <(iamctl completion bash)
|
||||
## Write bash completion code to a file and source if from .bash_profile
|
||||
iamctl completion bash > ~/.iam/completion.bash.inc
|
||||
printf "
|
||||
# IAM shell completion
|
||||
source '$HOME/.iam/completion.bash.inc'
|
||||
" >> $HOME/.bash_profile
|
||||
source $HOME/.bash_profile
|
||||
|
||||
# Load the iamctl completion code for zsh[1] into the current shell
|
||||
source <(iamctl completion zsh)
|
||||
# Set the iamctl completion code for zsh[1] to autoload on startup
|
||||
iamctl completion zsh > "${fpath[1]}/_iamctl"`)
|
||||
)
|
||||
|
||||
var completionShells = map[string]func(out io.Writer, boilerPlate string, cmd *cobra.Command) error{
|
||||
"bash": runCompletionBash,
|
||||
"zsh": runCompletionZsh,
|
||||
}
|
||||
|
||||
// NewCmdCompletion creates the `completion` command.
|
||||
func NewCmdCompletion(out io.Writer, boilerPlate string) *cobra.Command {
|
||||
shells := []string{}
|
||||
for s := range completionShells {
|
||||
shells = append(shells, s)
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "completion SHELL",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Output shell completion code for the specified shell (bash or zsh)",
|
||||
Long: completionLong,
|
||||
Example: completionExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := RunCompletion(out, boilerPlate, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
ValidArgs: shells,
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunCompletion checks given arguments and executes command.
|
||||
func RunCompletion(out io.Writer, boilerPlate string, cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "Shell not specified.")
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
return cmdutil.UsageErrorf(cmd, "Too many arguments. Expected only the shell type.")
|
||||
}
|
||||
|
||||
run, found := completionShells[args[0]]
|
||||
if !found {
|
||||
return cmdutil.UsageErrorf(cmd, "Unsupported shell type %q.", args[0])
|
||||
}
|
||||
|
||||
return run(out, boilerPlate, cmd.Parent())
|
||||
}
|
||||
|
||||
func runCompletionBash(out io.Writer, boilerPlate string, iamctl *cobra.Command) error {
|
||||
if len(boilerPlate) == 0 {
|
||||
boilerPlate = defaultBoilerPlate
|
||||
}
|
||||
|
||||
if _, err := out.Write([]byte(boilerPlate)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return iamctl.GenBashCompletion(out)
|
||||
}
|
||||
|
||||
func runCompletionZsh(out io.Writer, boilerPlate string, iamctl *cobra.Command) error {
|
||||
zshHead := "#compdef iamctl\n"
|
||||
|
||||
if _, err := out.Write([]byte(zshHead)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(boilerPlate) == 0 {
|
||||
boilerPlate = defaultBoilerPlate
|
||||
}
|
||||
|
||||
if _, err := out.Write([]byte(boilerPlate)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zshInitialization := `
|
||||
__iamctl_bash_source() {
|
||||
alias shopt=':'
|
||||
emulate -L sh
|
||||
setopt kshglob noshglob braceexpand
|
||||
|
||||
source "$@"
|
||||
}
|
||||
|
||||
__iamctl_type() {
|
||||
# -t is not supported by zsh
|
||||
if [ "$1" == "-t" ]; then
|
||||
shift
|
||||
|
||||
# fake Bash 4 to disable "complete -o nospace". Instead
|
||||
# "compopt +-o nospace" is used in the code to toggle trailing
|
||||
# spaces. We don't support that, but leave trailing spaces on
|
||||
# all the time
|
||||
if [ "$1" = "__iamctl_compopt" ]; then
|
||||
echo builtin
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
type "$@"
|
||||
}
|
||||
|
||||
__iamctl_compgen() {
|
||||
local completions w
|
||||
completions=( $(compgen "$@") ) || return $?
|
||||
|
||||
# filter by given word as prefix
|
||||
while [[ "$1" = -* && "$1" != -- ]]; do
|
||||
shift
|
||||
shift
|
||||
done
|
||||
if [[ "$1" == -- ]]; then
|
||||
shift
|
||||
fi
|
||||
for w in "${completions[@]}"; do
|
||||
if [[ "${w}" = "$1"* ]]; then
|
||||
echo "${w}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
__iamctl_compopt() {
|
||||
true # don't do anything. Not supported by bashcompinit in zsh
|
||||
}
|
||||
|
||||
__iamctl_ltrim_colon_completions()
|
||||
{
|
||||
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
|
||||
# Remove colon-word prefix from COMPREPLY items
|
||||
local colon_word=${1%${1##*:}}
|
||||
local i=${#COMPREPLY[*]}
|
||||
while [[ $((--i)) -ge 0 ]]; do
|
||||
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
__iamctl_get_comp_words_by_ref() {
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[${COMP_CWORD}-1]}"
|
||||
words=("${COMP_WORDS[@]}")
|
||||
cword=("${COMP_CWORD[@]}")
|
||||
}
|
||||
|
||||
__iamctl_filedir() {
|
||||
# Don't need to do anything here.
|
||||
# Otherwise we will get trailing space without "compopt -o nospace"
|
||||
true
|
||||
}
|
||||
|
||||
autoload -U +X bashcompinit && bashcompinit
|
||||
|
||||
# use word boundary patterns for BSD or GNU sed
|
||||
LWORD='[[:<:]]'
|
||||
RWORD='[[:>:]]'
|
||||
if sed --help 2>&1 | grep -q 'GNU\|BusyBox'; then
|
||||
LWORD='\<'
|
||||
RWORD='\>'
|
||||
fi
|
||||
|
||||
__iamctl_convert_bash_to_zsh() {
|
||||
sed \
|
||||
-e 's/declare -F/whence -w/' \
|
||||
-e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \
|
||||
-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \
|
||||
-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \
|
||||
-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \
|
||||
-e "s/${LWORD}_filedir${RWORD}/__iamctl_filedir/g" \
|
||||
-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__iamctl_get_comp_words_by_ref/g" \
|
||||
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__iamctl_ltrim_colon_completions/g" \
|
||||
-e "s/${LWORD}compgen${RWORD}/__iamctl_compgen/g" \
|
||||
-e "s/${LWORD}compopt${RWORD}/__iamctl_compopt/g" \
|
||||
-e "s/${LWORD}declare${RWORD}/builtin declare/g" \
|
||||
-e "s/\\\$(type${RWORD}/\$(__iamctl_type/g" \
|
||||
<<'BASH_COMPLETION_EOF'
|
||||
`
|
||||
if _, err := out.Write([]byte(zshInitialization)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := iamctl.GenBashCompletion(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := out.Write(buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zshTail := `
|
||||
BASH_COMPLETION_EOF
|
||||
}
|
||||
|
||||
__iamctl_bash_source <(__iamctl_convert_bash_to_zsh)
|
||||
`
|
||||
if _, err := out.Write([]byte(zshTail)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package info print the host information.
|
||||
package info
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
hoststat "github.com/likexian/host-stat-go"
|
||||
"github.com/openim-sigs/component-base/pkg/util/iputil"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
// Info defines the host information struct.
|
||||
type Info struct {
|
||||
HostName string
|
||||
IPAddress string
|
||||
OSRelease string
|
||||
CPUCore uint64
|
||||
MemTotal string
|
||||
MemFree string
|
||||
}
|
||||
|
||||
// InfoOptions is an options struct to support 'info' sub command.
|
||||
type InfoOptions struct {
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var infoExample = templates.Examples(`
|
||||
# Print the host information
|
||||
iamctl info`)
|
||||
|
||||
// NewInfoOptions returns an initialized InfoOptions instance.
|
||||
func NewInfoOptions(ioStreams genericclioptions.IOStreams) *InfoOptions {
|
||||
return &InfoOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdInfo returns new initialized instance of 'info' sub command.
|
||||
func NewCmdInfo(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewInfoOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "info",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Print the host information",
|
||||
Long: "Print the host information.",
|
||||
Example: infoExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Run executes an info sub command using the specified options.
|
||||
func (o *InfoOptions) Run(args []string) error {
|
||||
var info Info
|
||||
|
||||
hostInfo, err := hoststat.GetHostInfo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get host info failed!error:%w", err)
|
||||
}
|
||||
|
||||
info.HostName = hostInfo.HostName
|
||||
info.OSRelease = hostInfo.Release + " " + hostInfo.OSBit
|
||||
|
||||
memStat, err := hoststat.GetMemStat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get mem stat failed!error:%w", err)
|
||||
}
|
||||
|
||||
info.MemTotal = strconv.FormatUint(memStat.MemTotal, 10) + "M"
|
||||
info.MemFree = strconv.FormatUint(memStat.MemFree, 10) + "M"
|
||||
info.IPAddress = iputil.GetLocalIP()
|
||||
|
||||
cpuStat, err := hoststat.GetCPUInfo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get cpu stat failed!error:%w", err)
|
||||
}
|
||||
|
||||
info.CPUCore = cpuStat.CoreCount
|
||||
|
||||
s := reflect.ValueOf(&info).Elem()
|
||||
typeOfInfo := s.Type()
|
||||
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Field(i)
|
||||
|
||||
v := fmt.Sprintf("%v", f.Interface())
|
||||
if v != "" {
|
||||
fmt.Fprintf(o.Out, "%12s %v\n", typeOfInfo.Field(i).Name+":", f.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/openim-sigs/component-base/pkg/json"
|
||||
)
|
||||
|
||||
// ArgList defines a new pflag Value.
|
||||
type ArgList map[string]string
|
||||
|
||||
// String return value of ArgList in string format.
|
||||
func (l ArgList) String() string {
|
||||
data, _ := json.Marshal(l)
|
||||
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// Set sets the value of ArgList.
|
||||
func (l ArgList) Set(arg string) error {
|
||||
parts := strings.SplitN(arg, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid argument '%v'. Must use format 'key=value'. %v", arg, parts)
|
||||
}
|
||||
l[parts[0]] = parts[1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns the type name of ArgList.
|
||||
func (l ArgList) Type() string {
|
||||
return "map"
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package jwt can be used to sign/show/verify jwt token with given secretID and secretKey.
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
var jwtLong = templates.LongDesc(`
|
||||
JWT command.
|
||||
|
||||
This commands is used to sigin/show/verify jwt token.`)
|
||||
|
||||
// NewCmdJWT returns new initialized instance of 'jwt' sub command.
|
||||
func NewCmdJWT(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "jwt SUBCOMMAND",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "JWT command-line tool",
|
||||
Long: jwtLong,
|
||||
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
|
||||
}
|
||||
|
||||
// add subcommands
|
||||
cmd.AddCommand(NewCmdSign(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdShow(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdVerify(f, ioStreams))
|
||||
|
||||
return cmd
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/openim-sigs/component-base/pkg/json"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
showUsageStr = "show TOKEN"
|
||||
)
|
||||
|
||||
// ShowOptions is an options struct to support show subcommands.
|
||||
type ShowOptions struct {
|
||||
Compact bool
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
showExample = templates.Examples(`
|
||||
# Show header and Claims for a JWT token
|
||||
iamctl jwt show XXX.XXX.XXX`)
|
||||
|
||||
showUsageErrStr = fmt.Sprintf("expected '%s'.\nTOKEN is required arguments for the show command", showUsageStr)
|
||||
)
|
||||
|
||||
// NewShowOptions returns an initialized ShowOptions instance.
|
||||
func NewShowOptions(ioStreams genericclioptions.IOStreams) *ShowOptions {
|
||||
return &ShowOptions{
|
||||
Compact: false,
|
||||
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdShow returns new initialized instance of show sub command.
|
||||
func NewCmdShow(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewShowOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: showUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Show header and claims for a JWT token",
|
||||
Long: "Show header and claims for a JWT token",
|
||||
TraverseChildren: true,
|
||||
Example: showExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return cmdutil.UsageErrorf(cmd, showUsageErrStr)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// mark flag as deprecated
|
||||
cmd.Flags().BoolVar(&o.Compact, "compact", o.Compact, "output compact JSON.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *ShowOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *ShowOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a show subcommand using the specified options.
|
||||
func (o *ShowOptions) Run(args []string) error {
|
||||
// get the token
|
||||
tokenData := []byte(args[0])
|
||||
|
||||
// trim possible whitespace from token
|
||||
tokenData = regexp.MustCompile(`\s*$`).ReplaceAll(tokenData, []byte{})
|
||||
|
||||
token, err := jwt.Parse(string(tokenData), nil)
|
||||
if token == nil {
|
||||
return fmt.Errorf("malformed token: %w", err)
|
||||
}
|
||||
|
||||
// Print the token details
|
||||
fmt.Println("Header:")
|
||||
if err := printJSON(o.Compact, token.Header); err != nil {
|
||||
return fmt.Errorf("failed to output header: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Claims:")
|
||||
if err := printJSON(o.Compact, token.Claims); err != nil {
|
||||
return fmt.Errorf("failed to output claims: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// printJSON print a json object in accordance with the prophecy (or the command line options).
|
||||
func printJSON(compact bool, j interface{}) error {
|
||||
var out []byte
|
||||
var err error
|
||||
|
||||
if !compact {
|
||||
out, err = json.MarshalIndent(j, "", " ")
|
||||
} else {
|
||||
out, err = json.Marshal(j)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/internal/pkg/middleware/auth"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
signUsageStr = "sign SECRETID SECRETKEY"
|
||||
)
|
||||
|
||||
// ErrSigningMethod defines invalid signing method error.
|
||||
var ErrSigningMethod = errors.New("invalid signing method")
|
||||
|
||||
// SignOptions is an options struct to support sign subcommands.
|
||||
type SignOptions struct {
|
||||
Timeout time.Duration
|
||||
NotBefore time.Duration
|
||||
Algorithm string
|
||||
Audience string
|
||||
Issuer string
|
||||
Claims ArgList
|
||||
Head ArgList
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
signExample = templates.Examples(`
|
||||
# Sign a token with secretID and secretKey
|
||||
iamctl sign tgydj8d9EQSnFqKf iBdEdFNBLN1nR3fV
|
||||
|
||||
# Sign a token with expires and sign method
|
||||
iamctl sign tgydj8d9EQSnFqKf iBdEdFNBLN1nR3fV --timeout=2h --algorithm=HS256`)
|
||||
|
||||
signUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nSECRETID and SECRETKEY are required arguments for the sign command",
|
||||
signUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewSignOptions returns an initialized SignOptions instance.
|
||||
func NewSignOptions(ioStreams genericclioptions.IOStreams) *SignOptions {
|
||||
return &SignOptions{
|
||||
Timeout: 2 * time.Hour,
|
||||
Algorithm: "HS256",
|
||||
Audience: auth.AuthzAudience,
|
||||
Issuer: "iamctl",
|
||||
Claims: make(ArgList),
|
||||
Head: make(ArgList),
|
||||
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdSign returns new initialized instance of sign sub command.
|
||||
func NewCmdSign(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewSignOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: signUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Sign a jwt token with given secretID and secretKey",
|
||||
Long: "Sign a jwt token with given secretID and secretKey",
|
||||
TraverseChildren: true,
|
||||
Example: signExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return cmdutil.UsageErrorf(cmd, signUsageErrStr)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// mark flag as deprecated
|
||||
cmd.Flags().DurationVar(&o.Timeout, "timeout", o.Timeout, "JWT token expires time.")
|
||||
cmd.Flags().DurationVar(
|
||||
&o.NotBefore,
|
||||
"not-before",
|
||||
o.NotBefore,
|
||||
"Identifies the time before which the JWT MUST NOT be accepted for processing.",
|
||||
)
|
||||
cmd.Flags().StringVar(
|
||||
&o.Algorithm,
|
||||
"algorithm",
|
||||
o.Algorithm,
|
||||
"Signing algorithm - possible values are HS256, HS384, HS512.",
|
||||
)
|
||||
cmd.Flags().StringVar(
|
||||
&o.Audience,
|
||||
"audience",
|
||||
o.Audience,
|
||||
"Identifies the recipients that the JWT is intended for.",
|
||||
)
|
||||
cmd.Flags().StringVar(&o.Issuer, "issuer", o.Issuer, "Identifies the principal that issued the JWT.")
|
||||
cmd.Flags().Var(&o.Claims, "claim", "Add additional claims. may be used more than once.")
|
||||
cmd.Flags().Var(&o.Head, "header", "Add additional header params. may be used more than once.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *SignOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *SignOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
switch o.Algorithm {
|
||||
case "HS256", "HS384", "HS512":
|
||||
default:
|
||||
return ErrSigningMethod
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a sign subcommand using the specified options.
|
||||
func (o *SignOptions) Run(args []string) error {
|
||||
claims := jwt.MapClaims{
|
||||
"exp": time.Now().Add(o.Timeout).Unix(),
|
||||
"iat": time.Now().Unix(),
|
||||
"nbf": time.Now().Add(o.NotBefore).Unix(),
|
||||
"aud": o.Audience,
|
||||
"iss": o.Issuer,
|
||||
}
|
||||
|
||||
// add command line claims
|
||||
if len(o.Claims) > 0 {
|
||||
for k, v := range o.Claims {
|
||||
claims[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// create a new token
|
||||
token := jwt.NewWithClaims(jwt.GetSigningMethod(o.Algorithm), claims)
|
||||
|
||||
// add command line headers
|
||||
if len(o.Head) > 0 {
|
||||
for k, v := range o.Head {
|
||||
token.Header[k] = v
|
||||
}
|
||||
}
|
||||
token.Header["kid"] = args[0]
|
||||
|
||||
tokenString, err := token.SignedString([]byte(args[1]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, tokenString+"\n")
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
veirfyUsageStr = "veirfy SECRETKEY TOKEN"
|
||||
)
|
||||
|
||||
// VerifyOptions is an options struct to support verify subcommands.
|
||||
type VerifyOptions struct {
|
||||
Compact bool
|
||||
Debug bool
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
verifyExample = templates.Examples(`
|
||||
# Verify a JWT token
|
||||
iamctl jwt verify XXX xxxxx.yyyyy.zzzzz`)
|
||||
|
||||
verifyUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nSECRETKEY and TOKEN are required arguments for the subcmd1 command",
|
||||
veirfyUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewVerifyOptions returns an initialized VerifyOptions instance.
|
||||
func NewVerifyOptions(ioStreams genericclioptions.IOStreams) *VerifyOptions {
|
||||
return &VerifyOptions{
|
||||
Compact: false,
|
||||
Debug: false,
|
||||
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdVerify returns new initialized instance of verify sub command.
|
||||
func NewCmdVerify(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewVerifyOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "verify",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{"sub2"},
|
||||
Short: "Verify a JWT token",
|
||||
Long: "Verify a JWT token",
|
||||
TraverseChildren: true,
|
||||
Example: verifyExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return cmdutil.UsageErrorf(cmd, verifyUsageErrStr)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// mark flag as deprecated
|
||||
cmd.Flags().BoolVar(&o.Compact, "compact", o.Compact, "Output compact JSON.")
|
||||
cmd.Flags().BoolVar(&o.Debug, "debug", o.Debug, "Print out all kinds of debug data.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *VerifyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *VerifyOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a verify subcommand using the specified options.
|
||||
func (o *VerifyOptions) Run(args []string) error {
|
||||
// get the token
|
||||
tokenData := []byte(args[1])
|
||||
|
||||
// trim possible whitespace from token
|
||||
tokenData = regexp.MustCompile(`\s*$`).ReplaceAll(tokenData, []byte{})
|
||||
|
||||
// Parse the token. Load the key from command line option
|
||||
token, err := jwt.Parse(string(tokenData), func(t *jwt.Token) (interface{}, error) {
|
||||
return []byte(args[0]), nil
|
||||
})
|
||||
|
||||
// Print some debug data
|
||||
if o.Debug && token != nil {
|
||||
fmt.Println("Header:")
|
||||
if pErr := printJSON(o.Compact, token.Header); pErr != nil {
|
||||
return fmt.Errorf("failed to output header: %w", pErr)
|
||||
}
|
||||
|
||||
fmt.Println("Claims:")
|
||||
if pErr := printJSON(o.Compact, token.Claims); pErr != nil {
|
||||
return fmt.Errorf("failed to output claims: %w", pErr)
|
||||
}
|
||||
}
|
||||
|
||||
// Print an error if we can't parse for some reason
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't parse token: %w", err)
|
||||
}
|
||||
|
||||
// Is token invalid?
|
||||
if !token.Valid {
|
||||
return fmt.Errorf("token is invalid")
|
||||
}
|
||||
|
||||
if !o.Debug {
|
||||
// Print the token details
|
||||
if err := printJSON(o.Compact, token.Claims); err != nil {
|
||||
return fmt.Errorf("failed to output claims: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,619 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package new used to generate demo command code.
|
||||
// nolint: predeclared
|
||||
package new
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/openim-sigs/component-base/pkg/util/fileutil"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
newUsageStr = "new CMD_NAME | CMD_NAME CMD_DESCRIPTION"
|
||||
)
|
||||
|
||||
var (
|
||||
newLong = templates.LongDesc(`Used to generate demo command source code file.
|
||||
|
||||
Can use this command generate a command template file, and do some modify based on your needs.
|
||||
This can improve your R&D efficiency.`)
|
||||
|
||||
newExample = templates.Examples(`
|
||||
# Create a default 'test' command file without a description
|
||||
iamctl new test
|
||||
|
||||
# Create a default 'test' command file in /tmp/
|
||||
iamctl new test -d /tmp/
|
||||
|
||||
# Create a default 'test' command file with a description
|
||||
iamctl new test "This is a test command"
|
||||
|
||||
# Create command 'test' with two subcommands
|
||||
iamctl new -g test "This is a test command with two subcommands"`)
|
||||
|
||||
newUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nat least CMD_NAME is a required argument for the new command",
|
||||
newUsageStr,
|
||||
)
|
||||
|
||||
cmdTemplate = `package {{.CommandName}}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
{{.CommandName}}UsageStr = "{{.CommandName}} USERNAME PASSWORD"
|
||||
maxStringLength = 17
|
||||
)
|
||||
|
||||
// {{.CommandFunctionName}}Options is an options struct to support '{{.CommandName}}' sub command.
|
||||
type {{.CommandFunctionName}}Options struct {
|
||||
// options
|
||||
StringOption string
|
||||
StringSliceOption []string
|
||||
IntOption int
|
||||
BoolOption bool
|
||||
|
||||
// args
|
||||
Username string
|
||||
Password string
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
{{.CommandName}}Long = templates.LongDesc({{.Dot}}A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.{{.Dot}})
|
||||
|
||||
{{.CommandName}}Example = templates.Examples({{.Dot}}
|
||||
# Print all option values for {{.CommandName}}
|
||||
iamctl {{.CommandName}} marmotedu marmotedupass{{.Dot}})
|
||||
|
||||
{{.CommandName}}UsageErrStr = fmt.Sprintf("expected '%s'.\nUSERNAME and PASSWORD are required arguments for the {{.CommandName}} command", {{.CommandName}}UsageStr)
|
||||
)
|
||||
|
||||
// New{{.CommandFunctionName}}Options returns an initialized {{.CommandFunctionName}}Options instance.
|
||||
func New{{.CommandFunctionName}}Options(ioStreams genericclioptions.IOStreams) *{{.CommandFunctionName}}Options {
|
||||
return &{{.CommandFunctionName}}Options{
|
||||
StringOption: "default",
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmd{{.CommandFunctionName}} returns new initialized instance of '{{.CommandName}}' sub command.
|
||||
func NewCmd{{.CommandFunctionName}}(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := New{{.CommandFunctionName}}Options(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: {{.CommandName}}UsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "{{.CommandDescription}}",
|
||||
TraverseChildren: true,
|
||||
Long: {{.CommandName}}Long,
|
||||
Example: {{.CommandName}}Example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
// nolint: gomnd // no need
|
||||
if len(args) < 2 {
|
||||
return cmdutil.UsageErrorf(cmd, {{.CommandName}}UsageErrStr)
|
||||
}
|
||||
|
||||
// if need args equal to zero, uncomment the following code
|
||||
/*
|
||||
if len(args) != 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
|
||||
}
|
||||
*/
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// mark flag as deprecated
|
||||
_ = cmd.Flags().MarkDeprecated("deprecated-opt", "This flag is deprecated and will be removed in future.")
|
||||
cmd.Flags().StringVarP(&o.StringOption, "string", "", o.StringOption, "String option.")
|
||||
cmd.Flags().StringSliceVar(&o.StringSliceOption, "slice", o.StringSliceOption, "String slice option.")
|
||||
cmd.Flags().IntVarP(&o.IntOption, "int", "i", o.IntOption, "Int option.")
|
||||
cmd.Flags().BoolVarP(&o.BoolOption, "bool", "b", o.BoolOption, "Bool option.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *{{.CommandFunctionName}}Options) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
if o.StringOption != "" {
|
||||
o.StringOption += "(complete)"
|
||||
}
|
||||
|
||||
o.Username = args[0]
|
||||
o.Password = args[1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *{{.CommandFunctionName}}Options) Validate(cmd *cobra.Command, args []string) error {
|
||||
if len(o.StringOption) > maxStringLength {
|
||||
return cmdutil.UsageErrorf(cmd, "--string length must less than 18")
|
||||
}
|
||||
|
||||
if o.IntOption < 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "--int must be a positive integer: %v", o.IntOption)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a {{.CommandName}} sub command using the specified options.
|
||||
func (o *{{.CommandFunctionName}}Options) Run(args []string) error {
|
||||
fmt.Fprintf(o.Out, "The following is option values:\n")
|
||||
fmt.Fprintf(o.Out, "==> --string: %v\n==> --slice: %v\n==> --int: %v\n==> --bool: %v\n",
|
||||
o.StringOption, o.StringSliceOption, o.IntOption, o.BoolOption)
|
||||
|
||||
fmt.Fprintf(o.Out, "\nThe following is args values:\n")
|
||||
fmt.Fprintf(o.Out, "==> username: %v\n==> password: %v\n", o.Username, o.Password)
|
||||
|
||||
return nil
|
||||
}
|
||||
`
|
||||
|
||||
maincmdTemplate = `package {{.CommandName}}
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const maxStringLength = 17
|
||||
|
||||
var (
|
||||
{{.CommandName}}Long = templates.LongDesc({{.Dot}}
|
||||
Demo command.
|
||||
|
||||
This commands show you how to implement a command with two sub commands.{{.Dot}})
|
||||
)
|
||||
|
||||
// NewCmd{{.CommandFunctionName}} returns new initialized instance of '{{.CommandName}}' sub command.
|
||||
func NewCmd{{.CommandFunctionName}}(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "{{.CommandName}} SUBCOMMAND",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "{{.CommandDescription}}",
|
||||
Long: {{.CommandName}}Long,
|
||||
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
|
||||
}
|
||||
|
||||
// add subcommands
|
||||
cmd.AddCommand(NewCmdSubCmd1(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdSubCmd2(f, ioStreams))
|
||||
|
||||
// add persistent flags for '{{.CommandName}}'
|
||||
cmdutil.AddCleanFlags(cmd)
|
||||
|
||||
// persistent flag, we can get the value in subcommand via {{.Dot}}viper.Get{{.Dot}}
|
||||
cmd.PersistentFlags().StringP("persistent", "p", "this is a persistent option", "Cobra persistent option.")
|
||||
|
||||
// bind flags with viper
|
||||
viper.BindPFlag("persistent", cmd.PersistentFlags().Lookup("persistent"))
|
||||
|
||||
return cmd
|
||||
}
|
||||
`
|
||||
subcmd1Template = `package {{.CommandName}}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
subcmd1UsageStr = "subcmd1 USERNAME PASSWORD"
|
||||
)
|
||||
|
||||
// SubCmd1Options is an options struct to support subcmd1 subcommands.
|
||||
type SubCmd1Options struct {
|
||||
// options
|
||||
StringOption string
|
||||
StringSliceOption []string
|
||||
IntOption int
|
||||
BoolOption bool
|
||||
PersistentOption string
|
||||
|
||||
// args
|
||||
Username string
|
||||
Password string
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
subcmd1Long = templates.LongDesc({{.Dot}}A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.{{.Dot}})
|
||||
|
||||
subcmd1Example = templates.Examples({{.Dot}}
|
||||
# Print all option values for subcmd1
|
||||
iamctl {{.CommandName}} subcmd1 marmotedu marmotedupass
|
||||
|
||||
# Print all option values for subcmd1 with --persistent specified
|
||||
iamctl {{.CommandName}} subcmd1 marmotedu marmotedupass --persistent="specified persistent option in command line"{{.Dot}})
|
||||
|
||||
subcmd1UsageErrStr = fmt.Sprintf("expected '%s'.\nUSERNAME and PASSWORD are required arguments for the subcmd1 command", subcmd1UsageStr)
|
||||
)
|
||||
|
||||
// NewSubCmd1Options returns an initialized SubCmd1Options instance.
|
||||
func NewSubCmd1Options(ioStreams genericclioptions.IOStreams) *SubCmd1Options {
|
||||
return &SubCmd1Options{
|
||||
StringOption: "default",
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdSubCmd1 returns new initialized instance of subcmd1 sub command.
|
||||
func NewCmdSubCmd1(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewSubCmd1Options(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "subcmd1 USERNAME PASSWORD",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{"sub1"},
|
||||
Short: "A brief description of your command",
|
||||
TraverseChildren: true,
|
||||
Long: subcmd1Long,
|
||||
Example: subcmd1Example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
// nolint: gomnd // no need
|
||||
if len(args) < 2 {
|
||||
return cmdutil.UsageErrorf(cmd, subcmd1UsageErrStr)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// mark flag as deprecated
|
||||
_ = cmd.Flags().MarkDeprecated("deprecated-opt", "This flag is deprecated and will be removed in future.")
|
||||
cmd.Flags().StringVarP(&o.StringOption, "string", "", o.StringOption, "String option.")
|
||||
cmd.Flags().StringSliceVar(&o.StringSliceOption, "slice", o.StringSliceOption, "String slice option.")
|
||||
cmd.Flags().IntVarP(&o.IntOption, "int", "i", o.IntOption, "Int option.")
|
||||
cmd.Flags().BoolVarP(&o.BoolOption, "bool", "b", o.BoolOption, "Bool option.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *SubCmd1Options) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
if o.StringOption != "" {
|
||||
o.StringOption += "(complete)"
|
||||
}
|
||||
|
||||
o.PersistentOption = viper.GetString("persistent")
|
||||
o.Username = args[0]
|
||||
o.Password = args[1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *SubCmd1Options) Validate(cmd *cobra.Command, args []string) error {
|
||||
if len(o.StringOption) > maxStringLength {
|
||||
return cmdutil.UsageErrorf(cmd, "--string length must less than 18")
|
||||
}
|
||||
|
||||
if o.IntOption < 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "--int must be a positive integer: %v", o.IntOption)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a subcmd1 subcommand using the specified options.
|
||||
func (o *SubCmd1Options) Run(args []string) error {
|
||||
fmt.Fprintf(o.Out, "The following is option values:\n")
|
||||
fmt.Fprintf(o.Out, "==> --string: %v\n==> --slice: %v\n==> --int: %v\n==> --bool: %v\n==> --persistent: %v\n",
|
||||
o.StringOption, o.StringSliceOption, o.IntOption, o.BoolOption, o.PersistentOption)
|
||||
|
||||
fmt.Fprintf(o.Out, "\nThe following is args values:\n")
|
||||
fmt.Fprintf(o.Out, "==> username: %v\n==> password: %v\n", o.Username, o.Password)
|
||||
return nil
|
||||
}
|
||||
`
|
||||
subcmd2Template = `package {{.CommandName}}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
// SubCmd2Options is an options struct to support subcmd2 subcommands.
|
||||
type SubCmd2Options struct {
|
||||
StringOption string
|
||||
StringSliceOption []string
|
||||
IntOption int
|
||||
BoolOption bool
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
subcmd2Long = templates.LongDesc({{.Dot}}A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.{{.Dot}})
|
||||
|
||||
subcmd2Example = templates.Examples({{.Dot}}
|
||||
# Print all option values for subcmd2
|
||||
iamctl {{.CommandName}} subcmd2{{.Dot}})
|
||||
)
|
||||
|
||||
// NewSubCmd2Options returns an initialized SubCmd2Options instance.
|
||||
func NewSubCmd2Options(ioStreams genericclioptions.IOStreams) *SubCmd2Options {
|
||||
return &SubCmd2Options{
|
||||
StringOption: "default",
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdSubCmd2 returns new initialized instance of subcmd2 sub command.
|
||||
func NewCmdSubCmd2(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewSubCmd2Options(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "subcmd2",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{"sub2"},
|
||||
Short: "A brief description of your command",
|
||||
TraverseChildren: true,
|
||||
Long: subcmd2Long,
|
||||
Example: subcmd2Example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
// mark flag as deprecated
|
||||
cmd.Flags().StringVarP(&o.StringOption, "string", "", o.StringOption, "String option.")
|
||||
cmd.Flags().StringSliceVar(&o.StringSliceOption, "slice", o.StringSliceOption, "String slice option.")
|
||||
cmd.Flags().IntVarP(&o.IntOption, "int", "i", o.IntOption, "Int option.")
|
||||
cmd.Flags().BoolVarP(&o.BoolOption, "bool", "b", o.BoolOption, "Bool option.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *SubCmd2Options) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
|
||||
}
|
||||
|
||||
if o.StringOption != "" {
|
||||
o.StringOption += "(complete)"
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *SubCmd2Options) Validate(cmd *cobra.Command, args []string) error {
|
||||
if len(o.StringOption) > maxStringLength {
|
||||
return cmdutil.UsageErrorf(cmd, "--string length must less than 18")
|
||||
}
|
||||
|
||||
if o.IntOption < 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "--int must be a positive integer: %v", o.IntOption)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a subcmd2 subcommand using the specified options.
|
||||
func (o *SubCmd2Options) Run(args []string) error {
|
||||
fmt.Fprintf(o.Out, "The following is option values:\n")
|
||||
fmt.Fprintf(o.Out, "==> --string: %v\n==> --slice: %v\n==> --int: %v\n==> --bool: %v\n",
|
||||
o.StringOption, o.StringSliceOption, o.IntOption, o.BoolOption)
|
||||
return nil
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
// NewOptions is an options struct to support 'new' sub command.
|
||||
type NewOptions struct {
|
||||
Group bool
|
||||
Outdir string
|
||||
|
||||
// command template options, will render to command template
|
||||
CommandName string
|
||||
CommandDescription string
|
||||
CommandFunctionName string
|
||||
Dot string
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
// NewNewOptions returns an initialized NewOptions instance.
|
||||
func NewNewOptions(ioStreams genericclioptions.IOStreams) *NewOptions {
|
||||
return &NewOptions{
|
||||
Group: false,
|
||||
Outdir: ".",
|
||||
CommandDescription: "A brief description of your command",
|
||||
Dot: "`",
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdNew returns new initialized instance of 'new' sub command.
|
||||
func NewCmdNew(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewNewOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: newUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Generate demo command code",
|
||||
Long: newLong,
|
||||
Example: newExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
Aliases: []string{},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVarP(&o.Group, "group", "g", o.Group, "Generate two subcommands.")
|
||||
cmd.Flags().StringVarP(&o.Outdir, "outdir", "d", o.Outdir, "Where to create demo command files.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *NewOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return cmdutil.UsageErrorf(cmd, newUsageErrStr)
|
||||
}
|
||||
|
||||
o.CommandName = strings.ToLower(args[0])
|
||||
if len(args) > 1 {
|
||||
o.CommandDescription = args[1]
|
||||
}
|
||||
|
||||
o.CommandFunctionName = cases.Title(language.English).String(o.CommandName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *NewOptions) Validate(cmd *cobra.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a new sub command using the specified options.
|
||||
func (o *NewOptions) Run(args []string) error {
|
||||
if o.Group {
|
||||
return o.CreateCommandWithSubCommands()
|
||||
}
|
||||
|
||||
return o.CreateCommand()
|
||||
}
|
||||
|
||||
// CreateCommand create the command with options.
|
||||
func (o *NewOptions) CreateCommand() error {
|
||||
return o.GenerateGoCode(o.CommandName+".go", cmdTemplate)
|
||||
}
|
||||
|
||||
// CreateCommandWithSubCommands create sub commands with options.
|
||||
func (o *NewOptions) CreateCommandWithSubCommands() error {
|
||||
if err := o.GenerateGoCode(o.CommandName+".go", maincmdTemplate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := o.GenerateGoCode(o.CommandName+"_subcmd1.go", subcmd1Template); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := o.GenerateGoCode(o.CommandName+"_subcmd2.go", subcmd2Template); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateGoCode generate go source file.
|
||||
func (o *NewOptions) GenerateGoCode(name, codeTemplate string) error {
|
||||
tmpl, err := template.New("cmd").Parse(codeTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = fileutil.EnsureDirAll(o.Outdir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := filepath.Join(o.Outdir, name)
|
||||
fd, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
err = tmpl.Execute(fd, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Command file generated: %s\n", filename)
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package options print a list of global command-line options (applies to all commands).
|
||||
package options
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
)
|
||||
|
||||
var optionsExample = templates.Examples(`
|
||||
# Print flags inherited by all commands
|
||||
iamctl options`)
|
||||
|
||||
// NewCmdOptions implements the options command.
|
||||
func NewCmdOptions(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "options",
|
||||
Short: "Print the list of flags inherited by all commands",
|
||||
Long: "Print the list of flags inherited by all commands",
|
||||
Example: optionsExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
_ = cmd.Usage()
|
||||
},
|
||||
}
|
||||
|
||||
// The `options` command needs write its output to the `out` stream
|
||||
// (typically stdout). Without calling SetOutput here, the Usage()
|
||||
// function call will fall back to stderr.
|
||||
cmd.SetOutput(out)
|
||||
|
||||
templates.UseOptionsTemplates(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package policy provides functions to manage authorization policies on iam platform.
|
||||
package policy
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
var policyLong = templates.LongDesc(`
|
||||
Authorization policy management commands.
|
||||
|
||||
This commands allow you to manage your authorization policy on iam platform.`)
|
||||
|
||||
// NewCmdPolicy returns new initialized instance of 'policy' sub command.
|
||||
func NewCmdPolicy(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "policy SUBCOMMAND",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Manage authorization policies on iam platform",
|
||||
Long: policyLong,
|
||||
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
|
||||
}
|
||||
|
||||
cmd.AddCommand(NewCmdCreate(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdGet(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdList(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdDelete(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdUpdate(f, ioStreams))
|
||||
|
||||
return cmd
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "github.com/marmotedu/api/apiserver/v1"
|
||||
apiclientv1 "github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam/apiserver/v1"
|
||||
"github.com/openim-sigs/component-base/pkg/json"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/ory/ladon"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
createUsageStr = "create POLICY_NAME POLICY"
|
||||
)
|
||||
|
||||
// CreateOptions is an options struct to support create subcommands.
|
||||
type CreateOptions struct {
|
||||
Policy *v1.Policy
|
||||
|
||||
Client apiclientv1.APIV1Interface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
createExample = templates.Examples(`
|
||||
# Create a authorization policy
|
||||
iamctl policy create foo '{"description":"This is a updated policy","subjects":["users:<peter|ken>","users:maria","groups:admins"],"actions":["delete","<create|update>"],"effect":"allow","resources":["resources:articles:<.*>","resources:printer"],"conditions":{"remoteIPAddress":{"type":"CIDRCondition","options":{"cidr":"192.168.0.1/16"}}}}'`)
|
||||
|
||||
createUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nPOLICY_NAME and POLICY are required arguments for the create command",
|
||||
createUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewCreateOptions returns an initialized CreateOptions instance.
|
||||
func NewCreateOptions(ioStreams genericclioptions.IOStreams) *CreateOptions {
|
||||
return &CreateOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdCreate returns new initialized instance of create sub command.
|
||||
func NewCmdCreate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewCreateOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: createUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Create a authorization policy resource",
|
||||
TraverseChildren: true,
|
||||
Long: "Create a authorization policy resource.",
|
||||
Example: createExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 2 {
|
||||
return cmdutil.UsageErrorf(cmd, createUsageErrStr)
|
||||
}
|
||||
|
||||
var pol ladon.DefaultPolicy
|
||||
if err := json.Unmarshal([]byte(args[1]), &pol); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.Policy = &v1.Policy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: args[0],
|
||||
},
|
||||
Policy: v1.AuthzPolicy{
|
||||
DefaultPolicy: pol,
|
||||
},
|
||||
}
|
||||
|
||||
clientConfig, err := f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Client, err = apiclientv1.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *CreateOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
if errs := o.Policy.Validate(); len(errs) != 0 {
|
||||
return errs.ToAggregate()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a create subcommand using the specified options.
|
||||
func (o *CreateOptions) Run(args []string) error {
|
||||
ret, err := o.Client.Policies().Create(context.TODO(), o.Policy, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "policy/%s created\n", ret.Name)
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
deleteUsageStr = "delete POLICY_NAME"
|
||||
)
|
||||
|
||||
// DeleteOptions is an options struct to support delete subcommands.
|
||||
type DeleteOptions struct {
|
||||
Name string
|
||||
|
||||
iamclient iam.IamInterface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
deleteExample = templates.Examples(`
|
||||
# Delete a policy resource
|
||||
iamctl policy delete foo`)
|
||||
|
||||
deleteUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nPOLICY_NAME is required arguments for the delete command",
|
||||
deleteUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewDeleteOptions returns an initialized DeleteOptions instance.
|
||||
func NewDeleteOptions(ioStreams genericclioptions.IOStreams) *DeleteOptions {
|
||||
return &DeleteOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdDelete returns new initialized instance of delete sub command.
|
||||
func NewCmdDelete(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewDeleteOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Delete a authorization policy resource",
|
||||
TraverseChildren: true,
|
||||
Long: "Delete a authorization policy resource.",
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *DeleteOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, deleteUsageErrStr)
|
||||
}
|
||||
|
||||
o.Name = args[0]
|
||||
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *DeleteOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a delete subcommand using the specified options.
|
||||
func (o *DeleteOptions) Run() error {
|
||||
if err := o.iamclient.APIV1().Policies().Delete(context.TODO(), o.Name, metav1.DeleteOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "policy/%s deleted\n", o.Name)
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package policy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
"github.com/openim-sigs/component-base/pkg/json"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
getUsageStr = "get POLICY_NAME"
|
||||
)
|
||||
|
||||
// GetOptions is an options struct to support get subcommands.
|
||||
type GetOptions struct {
|
||||
Name string
|
||||
|
||||
iamclient iam.IamInterface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
getExample = templates.Examples(`
|
||||
# Display a policy resource
|
||||
iamctl policy get foo`)
|
||||
|
||||
getUsageErrStr = fmt.Sprintf("expected '%s'.\nPOLICY_NAME is required arguments for the get command", getUsageStr)
|
||||
)
|
||||
|
||||
// NewGetOptions returns an initialized GetOptions instance.
|
||||
func NewGetOptions(ioStreams genericclioptions.IOStreams) *GetOptions {
|
||||
return &GetOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdGet returns new initialized instance of get sub command.
|
||||
func NewCmdGet(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewGetOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: getUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Display a authorization policy resource",
|
||||
TraverseChildren: true,
|
||||
Long: "Display a authorization policy resource.",
|
||||
Example: getExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, getUsageErrStr)
|
||||
}
|
||||
|
||||
o.Name = args[0]
|
||||
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *GetOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a get subcommand using the specified options.
|
||||
func (o *GetOptions) Run(args []string) error {
|
||||
policy, err := o.iamclient.APIV1().Policies().Get(context.TODO(), o.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bf := bytes.NewBuffer([]byte{})
|
||||
jsonEncoder := json.NewEncoder(bf)
|
||||
jsonEncoder.SetEscapeHTML(false)
|
||||
if err := jsonEncoder.Encode(policy.Policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "%12s %s\n", color.RedString(policy.Name+":"), bf.String())
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package policy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
"github.com/openim-sigs/component-base/pkg/json"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLimit = 1000
|
||||
)
|
||||
|
||||
// ListOptions is an options struct to support list subcommands.
|
||||
type ListOptions struct {
|
||||
Offset int64
|
||||
Limit int64
|
||||
|
||||
iamclient iam.IamInterface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var listExample = templates.Examples(`
|
||||
# Display all policy resources
|
||||
iamctl poicy list
|
||||
|
||||
# Display all policy resources with offset and limit
|
||||
iamctl policy list --offset=0 --limit=10`)
|
||||
|
||||
// NewListOptions returns an initialized ListOptions instance.
|
||||
func NewListOptions(ioStreams genericclioptions.IOStreams) *ListOptions {
|
||||
return &ListOptions{
|
||||
Offset: 0,
|
||||
Limit: defaultLimit,
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdList returns new initialized instance of list sub command.
|
||||
func NewCmdList(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewListOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Display all authorization policy resources",
|
||||
TraverseChildren: true,
|
||||
Long: "Display all authorization policy resources.",
|
||||
Example: listExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
cmd.Flags().Int64VarP(&o.Offset, "offset", "o", o.Offset, "Specify the offset of the first row to be returned.")
|
||||
cmd.Flags().Int64VarP(&o.Limit, "limit", "l", o.Limit, "Specify the amount records to be returned.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *ListOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *ListOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a list subcommand using the specified options.
|
||||
func (o *ListOptions) Run(args []string) error {
|
||||
policies, err := o.iamclient.APIV1().Policies().List(context.TODO(), metav1.ListOptions{
|
||||
Offset: &o.Offset,
|
||||
Limit: &o.Limit,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pol := range policies.Items {
|
||||
bf := bytes.NewBuffer([]byte{})
|
||||
jsonEncoder := json.NewEncoder(bf)
|
||||
jsonEncoder.SetEscapeHTML(false)
|
||||
if err := jsonEncoder.Encode(pol.Policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "%12s %s\n", color.RedString(pol.Name+":"), bf.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "github.com/marmotedu/api/apiserver/v1"
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
"github.com/openim-sigs/component-base/pkg/json"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/ory/ladon"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
updateUsageStr = "update POLICY_NAME POLICY"
|
||||
)
|
||||
|
||||
// UpdateOptions is an options struct to support update subcommands.
|
||||
type UpdateOptions struct {
|
||||
Policy *v1.Policy
|
||||
|
||||
iamclient iam.IamInterface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
updateExample = templates.Examples(`
|
||||
# Update a authorization policy with new policy.
|
||||
iamctl policy update foo "{"description":"This is a updated policy","subjects":["users:<peter|ken>","users:maria","groups:admins"],"actions":["delete","<create|update>"],"effect":"allow","resources":["resources:articles:<.*>","resources:printer"],"conditions":{"remoteIPAddress":{"type":"CIDRCondition","options":{"cidr":"192.168.0.1/16"}}}}"`)
|
||||
|
||||
updateUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nPOLICY_NAME and POLICY is required arguments for the update command",
|
||||
updateUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewUpdateOptions returns an initialized UpdateOptions instance.
|
||||
func NewUpdateOptions(ioStreams genericclioptions.IOStreams) *UpdateOptions {
|
||||
return &UpdateOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdUpdate returns new initialized instance of update sub command.
|
||||
func NewCmdUpdate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewUpdateOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: updateUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Update a authorization policy resource",
|
||||
TraverseChildren: true,
|
||||
Long: "Update a authorization policy resource.",
|
||||
Example: updateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *UpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
|
||||
if len(args) < 2 {
|
||||
return cmdutil.UsageErrorf(cmd, updateUsageErrStr)
|
||||
}
|
||||
|
||||
var pol ladon.DefaultPolicy
|
||||
if err = json.Unmarshal([]byte(args[1]), &pol); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.Policy = &v1.Policy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: args[0],
|
||||
},
|
||||
Policy: v1.AuthzPolicy{
|
||||
DefaultPolicy: pol,
|
||||
},
|
||||
}
|
||||
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *UpdateOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a update subcommand using the specified options.
|
||||
func (o *UpdateOptions) Run(args []string) error {
|
||||
ret, err := o.iamclient.APIV1().Policies().Update(context.TODO(), o.Policy, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "policy/%s updated\n", ret.Name)
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package secret provides functions to manage secrets on iam platform.
|
||||
package secret
|
||||
|
||||
import (
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
var secretLong = templates.LongDesc(`
|
||||
Secret management commands.
|
||||
|
||||
This commands allow you to manage your secret on iam platform.`)
|
||||
|
||||
// NewCmdSecret returns new initialized instance of 'secret' sub command.
|
||||
func NewCmdSecret(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "secret SUBCOMMAND",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Manage secrets on iam platform",
|
||||
Long: secretLong,
|
||||
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
|
||||
}
|
||||
|
||||
cmd.AddCommand(NewCmdCreate(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdGet(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdList(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdDelete(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdUpdate(f, ioStreams))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// setHeader set headers for secret commands.
|
||||
func setHeader(table *tablewriter.Table) *tablewriter.Table {
|
||||
table.SetHeader([]string{"Name", "SecretID", "SecretKey", "Expires", "Created"})
|
||||
table.SetHeaderColor(tablewriter.Colors{tablewriter.FgGreenColor},
|
||||
tablewriter.Colors{tablewriter.FgRedColor},
|
||||
tablewriter.Colors{tablewriter.FgCyanColor},
|
||||
tablewriter.Colors{tablewriter.FgMagentaColor},
|
||||
tablewriter.Colors{tablewriter.FgGreenColor})
|
||||
|
||||
return table
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
v1 "github.com/marmotedu/api/apiserver/v1"
|
||||
apiclientv1 "github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam/apiserver/v1"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
createUsageStr = "create SECRET_NAME"
|
||||
)
|
||||
|
||||
// CreateOptions is an options struct to support create subcommands.
|
||||
type CreateOptions struct {
|
||||
Description string
|
||||
Expires int64
|
||||
|
||||
Secret *v1.Secret
|
||||
|
||||
Client apiclientv1.APIV1Interface
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
createLong = templates.LongDesc(`Create secret resource.
|
||||
|
||||
This will generate secretID and secretKey which can be used to sign JWT token.`)
|
||||
|
||||
createExample = templates.Examples(`
|
||||
# Create secret which will expired after 2 hours
|
||||
iamctl secret create foo
|
||||
|
||||
# Create secret with a specified expire time and description
|
||||
iamctl secret create foo --expires=1988121600 --description="secret for iam"`)
|
||||
|
||||
createUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nSECRET_NAME is required arguments for the create command",
|
||||
createUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewCreateOptions returns an initialized CreateOptions instance.
|
||||
func NewCreateOptions(ioStreams genericclioptions.IOStreams) *CreateOptions {
|
||||
return &CreateOptions{
|
||||
Expires: time.Now().Add(144 * time.Hour).Unix(),
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdCreate returns new initialized instance of create sub command.
|
||||
func NewCmdCreate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewCreateOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: createUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Create secret resource",
|
||||
TraverseChildren: true,
|
||||
Long: createLong,
|
||||
Example: createExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&o.Description, "description", o.Description, "The descriptin of the secret.")
|
||||
cmd.Flags().Int64Var(&o.Expires, "expires", o.Expires, "The expire time of the secret.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, createUsageErrStr)
|
||||
}
|
||||
|
||||
o.Secret = &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: args[0],
|
||||
},
|
||||
Expires: o.Expires,
|
||||
Description: o.Description,
|
||||
}
|
||||
|
||||
clientConfig, err := f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Client, err = apiclientv1.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *CreateOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
if errs := o.Secret.Validate(); len(errs) != 0 {
|
||||
return errs.ToAggregate()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a create subcommand using the specified options.
|
||||
func (o *CreateOptions) Run(args []string) error {
|
||||
secret, err := o.Client.Secrets().Create(context.TODO(), o.Secret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "secret/%s created\n", secret.Name)
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
deleteUsageStr = "delete SECRET_NAME"
|
||||
)
|
||||
|
||||
// DeleteOptions is an options struct to support delete subcommands.
|
||||
type DeleteOptions struct {
|
||||
Name string
|
||||
|
||||
genericclioptions.IOStreams
|
||||
|
||||
iamclient iam.IamInterface
|
||||
}
|
||||
|
||||
var (
|
||||
deleteExample = templates.Examples(`
|
||||
# Delete secret foo
|
||||
iamctl secret delete foo`)
|
||||
|
||||
deleteUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nSECRET_NAME is required arguments for the delete command",
|
||||
deleteUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewDeleteOptions returns an initialized DeleteOptions instance.
|
||||
func NewDeleteOptions(ioStreams genericclioptions.IOStreams) *DeleteOptions {
|
||||
return &DeleteOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdDelete returns new initialized instance of delete sub command.
|
||||
func NewCmdDelete(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewDeleteOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: deleteUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Delete a secret resource",
|
||||
TraverseChildren: true,
|
||||
Long: "Delete a secret resource.",
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *DeleteOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, deleteUsageErrStr)
|
||||
}
|
||||
|
||||
o.Name = args[0]
|
||||
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *DeleteOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a delete subcommand using the specified options.
|
||||
func (o *DeleteOptions) Run() error {
|
||||
if err := o.iamclient.APIV1().Secrets().Delete(context.TODO(), o.Name, metav1.DeleteOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "secret/%s deleted\n", o.Name)
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
getUsageStr = "get SECRET_NAME"
|
||||
)
|
||||
|
||||
// GetOptions is an options struct to support get subcommands.
|
||||
type GetOptions struct {
|
||||
Name string
|
||||
|
||||
iamclient iam.IamInterface
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
getExample = templates.Examples(`
|
||||
# Get a specified secret information
|
||||
iamctl secret get foo`)
|
||||
|
||||
getUsageErrStr = fmt.Sprintf("expected '%s'.\nSECRET_NAME is required arguments for the get command", getUsageStr)
|
||||
)
|
||||
|
||||
// NewGetOptions returns an initialized GetOptions instance.
|
||||
func NewGetOptions(ioStreams genericclioptions.IOStreams) *GetOptions {
|
||||
return &GetOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdGet returns new initialized instance of get sub command.
|
||||
func NewCmdGet(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewGetOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "get SECRET_NAME",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Display a secret resource",
|
||||
TraverseChildren: true,
|
||||
Long: "Display a secret resource.",
|
||||
Example: getExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, getUsageErrStr)
|
||||
}
|
||||
|
||||
o.Name = args[0]
|
||||
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *GetOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a get subcommand using the specified options.
|
||||
func (o *GetOptions) Run(args []string) error {
|
||||
secret, err := o.iamclient.APIV1().Secrets().Get(context.TODO(), o.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
table := tablewriter.NewWriter(o.Out)
|
||||
|
||||
data := [][]string{
|
||||
{
|
||||
secret.Name,
|
||||
secret.SecretID,
|
||||
secret.SecretKey,
|
||||
time.Unix(secret.Expires, 0).Format("2006-01-02 15:04:05"),
|
||||
secret.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
}
|
||||
|
||||
table = setHeader(table)
|
||||
table = cmdutil.TableWriterDefaultConfig(table)
|
||||
table.AppendBulk(data)
|
||||
table.Render()
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
defaltLimit = 1000
|
||||
)
|
||||
|
||||
// ListOptions is an options struct to support list subcommands.
|
||||
type ListOptions struct {
|
||||
Offset int64
|
||||
Limit int64
|
||||
|
||||
iamclient iam.IamInterface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var listExample = templates.Examples(`
|
||||
# List all secrets
|
||||
iamctl secret list
|
||||
|
||||
# List secrets with limit and offset
|
||||
iamctl secret list --offset=0 --limit=5`)
|
||||
|
||||
// NewListOptions returns an initialized ListOptions instance.
|
||||
func NewListOptions(ioStreams genericclioptions.IOStreams) *ListOptions {
|
||||
return &ListOptions{
|
||||
IOStreams: ioStreams,
|
||||
Offset: 0,
|
||||
Limit: defaltLimit,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdList returns new initialized instance of list sub command.
|
||||
func NewCmdList(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewListOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Display all secret resources",
|
||||
TraverseChildren: true,
|
||||
Long: "Display all secret resources.",
|
||||
Example: listExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
cmd.Flags().Int64VarP(&o.Offset, "offset", "o", o.Offset, "Specify the offset of the first row to be returned.")
|
||||
cmd.Flags().Int64VarP(&o.Limit, "limit", "l", o.Limit, "Specify the amount records to be returned.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *ListOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *ListOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a list subcommand using the specified options.
|
||||
func (o *ListOptions) Run(args []string) error {
|
||||
secrets, err := o.iamclient.APIV1().Secrets().List(context.TODO(), metav1.ListOptions{
|
||||
Offset: &o.Offset,
|
||||
Limit: &o.Limit,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make([][]string, 0, len(secrets.Items))
|
||||
table := tablewriter.NewWriter(o.Out)
|
||||
|
||||
for _, secret := range secrets.Items {
|
||||
data = append(data, []string{
|
||||
secret.Name,
|
||||
secret.SecretID,
|
||||
secret.SecretKey,
|
||||
time.Unix(secret.Expires, 0).Format("2006-01-02 15:04:05"),
|
||||
secret.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
table = setHeader(table)
|
||||
table = cmdutil.TableWriterDefaultConfig(table)
|
||||
table.AppendBulk(data)
|
||||
table.Render()
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "github.com/marmotedu/api/apiserver/v1"
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
updateUsageStr = "update SECRET_NAME"
|
||||
)
|
||||
|
||||
// UpdateOptions is an options struct to support update subcommands.
|
||||
type UpdateOptions struct {
|
||||
Description string
|
||||
Expires int64
|
||||
|
||||
Secret *v1.Secret
|
||||
|
||||
iamclient iam.IamInterface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
updateExample = templates.Examples(`
|
||||
# Update a secret resource
|
||||
iamctl secret update foo --expires=4h --description="new description"`)
|
||||
|
||||
updateUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nSECRET_NAME is required arguments for the update command",
|
||||
updateUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewUpdateOptions returns an initialized UpdateOptions instance.
|
||||
func NewUpdateOptions(ioStreams genericclioptions.IOStreams) *UpdateOptions {
|
||||
return &UpdateOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdUpdate returns new initialized instance of update sub command.
|
||||
func NewCmdUpdate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewUpdateOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "update SECRET_NAME",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Update a secret resource",
|
||||
TraverseChildren: true,
|
||||
Long: "Update a secret resource.",
|
||||
Example: updateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&o.Description, "description", o.Description, "The description of the secret.")
|
||||
cmd.Flags().Int64Var(&o.Expires, "expires", o.Expires, "The expires of the secret.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *UpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, updateUsageErrStr)
|
||||
}
|
||||
|
||||
o.Secret = &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: args[0],
|
||||
},
|
||||
Description: o.Description,
|
||||
Expires: o.Expires,
|
||||
}
|
||||
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *UpdateOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a update subcommand using the specified options.
|
||||
func (o *UpdateOptions) Run(args []string) error {
|
||||
secret, err := o.iamclient.APIV1().Secrets().Update(context.TODO(), o.Secret, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "secret/%s updated\n", secret.Name)
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package set used to set specific features on objects.
|
||||
package set
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
var setLong = templates.LongDesc(`
|
||||
Configure objects.
|
||||
|
||||
These commands help you make changes to existing objects.`)
|
||||
|
||||
// NewCmdSet returns an initialized Command instance for 'set' sub command.
|
||||
func NewCmdSet(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "set SUBCOMMAND",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Set specific features on objects",
|
||||
Long: setLong,
|
||||
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
|
||||
}
|
||||
|
||||
// add subcommands
|
||||
// cmd.AddCommand(NewCmdDB(f, ioStreams))
|
||||
|
||||
return cmd
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package set
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
// import mysql driver.
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
v1 "github.com/marmotedu/api/apiserver/v1"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
// DBOptions is an options struct to support 'db' sub command.
|
||||
type DBOptions struct {
|
||||
host string
|
||||
username string
|
||||
password string
|
||||
Database string
|
||||
|
||||
drop bool
|
||||
admin bool
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var setExample = templates.Examples(`
|
||||
# Create new iam platform database and tables
|
||||
iamctl set db --mysql.host=127.0.0.1:3306 --mysql.username=iam --mysql.password=iamxxxx --mysql.database=iam
|
||||
|
||||
# Create new iam platform database and tables with a administrator inserted
|
||||
iamctl set db --admin --mysql.host=127.0.0.1:3306 --mysql.username=iam --mysql.password=iamxxxx --mysql.database=iam
|
||||
|
||||
# drop and create iam platform database and tables
|
||||
iamctl set db -d --mysql.host=127.0.0.1:3306 --mysql.username=iam --mysql.password=iamxxxx --mysql.database=iam`)
|
||||
|
||||
// NewDBOptions returns an initialized DBOptions instance.
|
||||
func NewDBOptions(ioStreams genericclioptions.IOStreams) *DBOptions {
|
||||
return &DBOptions{
|
||||
host: "127.0.0.1:3306",
|
||||
username: "root",
|
||||
password: "root",
|
||||
Database: "iam",
|
||||
|
||||
drop: false,
|
||||
admin: false,
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdDB returns new initialized instance of 'db' sub command.
|
||||
func NewCmdDB(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewDBOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "db",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Initialize the iam database",
|
||||
Long: "Initialize the iam database.",
|
||||
Example: setExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete())
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
Aliases: []string{},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&o.host, "host", o.host, "MySQL service host address.")
|
||||
cmd.Flags().StringVar(&o.username, "username", o.username, "username for access to mysql service.")
|
||||
cmd.Flags().StringVar(&o.password, "password", o.password,
|
||||
"password for access to mysql, should be used pair with password.")
|
||||
cmd.Flags().StringVar(&o.Database, "database", o.Database, "Database name for the server to use.")
|
||||
cmd.Flags().BoolVarP(&o.drop, "drop", "d", o.drop, "drop database if exists, pls double check the db name!")
|
||||
cmd.Flags().BoolVar(&o.admin, "admin", o.drop, "Insert a administrator user to the database.")
|
||||
|
||||
_ = viper.BindPFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *DBOptions) Complete() error {
|
||||
// o.host = viper.GetString("host")
|
||||
// o.username = viper.GetString("username")
|
||||
// o.password = viper.GetString("password")
|
||||
// o.Database = viper.GetString("database")
|
||||
// o.drop = viper.GetBool("drop")
|
||||
// o.admin = viper.GetBool("admin")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *DBOptions) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a db sub command using the specified options.
|
||||
func (o *DBOptions) Run() error {
|
||||
if err := o.ensureSchema(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
|
||||
o.username, o.password, o.host, o.Database, true, "Local")
|
||||
|
||||
db, err := gorm.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if db.HasTable(&v1.User{}) {
|
||||
db.AutoMigrate(&v1.User{})
|
||||
} else {
|
||||
db.Debug().CreateTable(&v1.User{})
|
||||
}
|
||||
|
||||
if db.HasTable(&v1.Secret{}) {
|
||||
db.AutoMigrate(&v1.Secret{})
|
||||
} else {
|
||||
db.CreateTable(&v1.Secret{})
|
||||
}
|
||||
|
||||
if db.HasTable(&v1.Policy{}) {
|
||||
db.AutoMigrate(&v1.Policy{})
|
||||
} else {
|
||||
db.CreateTable(&v1.Policy{})
|
||||
}
|
||||
fmt.Fprintf(o.Out, "update table success\n")
|
||||
|
||||
if o.admin {
|
||||
if err := o.insertAdministrator(db); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *DBOptions) ensureSchema() error {
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s)/?charset=utf8", o.username, o.password, o.host)
|
||||
|
||||
db, err := gorm.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if o.drop {
|
||||
dropSQL := fmt.Sprintf("DROP DATABASE IF EXISTS %s", o.Database)
|
||||
if err := db.Exec(dropSQL).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(o.Out, "drop database %s success\n", o.Database)
|
||||
}
|
||||
|
||||
createSQL := fmt.Sprintf("CREATE DATABASE if not exists %s CHARSET utf8 COLLATE utf8_general_ci", o.Database)
|
||||
if err := db.Exec(createSQL).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "create database %s success\n", o.Database)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *DBOptions) insertAdministrator(db *gorm.DB) error {
|
||||
if !o.drop {
|
||||
return nil
|
||||
}
|
||||
|
||||
// insert administrator user
|
||||
user := v1.User{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "admin",
|
||||
},
|
||||
Nickname: "admin",
|
||||
Password: "Admin@2020",
|
||||
Email: "colin404@foxmail.com",
|
||||
Phone: "1812884xxxx",
|
||||
IsAdmin: 1,
|
||||
}
|
||||
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "insert administrator success\n")
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package user provides functions to manage users on iam platform.
|
||||
package user
|
||||
|
||||
import (
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
var userLong = templates.LongDesc(`
|
||||
User management commands.
|
||||
|
||||
Administrator can use all subcommands, non-administrator only allow to use create/get/upate. When call get/update non-administrator only allow to operate their own resources, if permission not allowed, will return an 'Permission denied' error.`)
|
||||
|
||||
// NewCmdUser returns new initialized instance of 'user' sub command.
|
||||
func NewCmdUser(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "user SUBCOMMAND",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Manage users on iam platform",
|
||||
Long: userLong,
|
||||
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
|
||||
}
|
||||
|
||||
cmd.AddCommand(NewCmdCreate(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdGet(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdList(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdDelete(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdUpdate(f, ioStreams))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// setHeader set headers for user commands.
|
||||
func setHeader(table *tablewriter.Table) *tablewriter.Table {
|
||||
table.SetHeader([]string{"Name", "Nickname", "Email", "Phone", "Created", "Updated"})
|
||||
table.SetHeaderColor(tablewriter.Colors{tablewriter.FgGreenColor},
|
||||
tablewriter.Colors{tablewriter.FgRedColor},
|
||||
tablewriter.Colors{tablewriter.FgCyanColor},
|
||||
tablewriter.Colors{tablewriter.FgMagentaColor},
|
||||
tablewriter.Colors{tablewriter.FgGreenColor},
|
||||
tablewriter.Colors{tablewriter.FgWhiteColor})
|
||||
|
||||
return table
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "github.com/marmotedu/api/apiserver/v1"
|
||||
apiclientv1 "github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam/apiserver/v1"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
createUsageStr = "create USERNAME PASSWORD EMAIL"
|
||||
)
|
||||
|
||||
// CreateOptions is an options struct to support create subcommands.
|
||||
type CreateOptions struct {
|
||||
Nickname string
|
||||
Phone string
|
||||
|
||||
User *v1.User
|
||||
|
||||
Client apiclientv1.APIV1Interface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
createLong = templates.LongDesc(`Create a user on iam platform.
|
||||
If nickname not specified, username will be used.`)
|
||||
|
||||
createExample = templates.Examples(`
|
||||
# Create user with given input
|
||||
iamctl user create foo Foo@2020 foo@foxmail.com
|
||||
|
||||
# Create user wt
|
||||
iamctl user create foo Foo@2020 foo@foxmail.com --phone=18128845xxx --nickname=colin`)
|
||||
|
||||
createUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nUSERNAME, PASSWORD and EMAIL are required arguments for the create command",
|
||||
createUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewCreateOptions returns an initialized CreateOptions instance.
|
||||
func NewCreateOptions(ioStreams genericclioptions.IOStreams) *CreateOptions {
|
||||
return &CreateOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdCreate returns new initialized instance of create sub command.
|
||||
func NewCmdCreate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewCreateOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: createUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Create a user resource",
|
||||
TraverseChildren: true,
|
||||
Long: createLong,
|
||||
Example: createExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
// mark flag as deprecated
|
||||
cmd.Flags().StringVar(&o.Nickname, "nickname", o.Nickname, "The nickname of the user.")
|
||||
cmd.Flags().StringVar(&o.Phone, "phone", o.Phone, "The phone number of the user.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
if len(args) < 3 {
|
||||
return cmdutil.UsageErrorf(cmd, createUsageErrStr)
|
||||
}
|
||||
|
||||
if o.Nickname == "" {
|
||||
o.Nickname = args[0]
|
||||
}
|
||||
|
||||
o.User = &v1.User{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: args[0],
|
||||
},
|
||||
Nickname: o.Nickname,
|
||||
Password: args[1],
|
||||
Email: args[2],
|
||||
Phone: o.Phone,
|
||||
}
|
||||
|
||||
clientConfig, err := f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Client, err = apiclientv1.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *CreateOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
if errs := o.User.Validate(); len(errs) != 0 {
|
||||
return errs.ToAggregate()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a create subcommand using the specified options.
|
||||
func (o *CreateOptions) Run(args []string) error {
|
||||
ret, err := o.Client.Users().Create(context.TODO(), o.User, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "user/%s created\n", ret.Name)
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
deleteUsageStr = "delete USERNAME"
|
||||
)
|
||||
|
||||
// DeleteOptions is an options struct to support delete subcommands.
|
||||
type DeleteOptions struct {
|
||||
Name string
|
||||
|
||||
iamclient iam.IamInterface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
deleteExample = templates.Examples(`
|
||||
# Delete user foo from platform
|
||||
iamctl user delete foo`)
|
||||
|
||||
deleteUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nUSERNAME is required arguments for the delete command",
|
||||
deleteUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewDeleteOptions returns an initialized DeleteOptions instance.
|
||||
func NewDeleteOptions(ioStreams genericclioptions.IOStreams) *DeleteOptions {
|
||||
return &DeleteOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdDelete returns new initialized instance of delete sub command.
|
||||
func NewCmdDelete(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewDeleteOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: deleteUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Delete a user resource from iam platform (Administrator rights required)",
|
||||
TraverseChildren: true,
|
||||
Long: "Delete a user resource from iam platform, only administrator can do this operation.",
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *DeleteOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, deleteUsageErrStr)
|
||||
}
|
||||
|
||||
o.Name = args[0]
|
||||
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *DeleteOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a delete subcommand using the specified options.
|
||||
func (o *DeleteOptions) Run() error {
|
||||
if err := o.iamclient.APIV1().Users().Delete(context.TODO(), o.Name, metav1.DeleteOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "user/%s deleted\n", o.Name)
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
getUsageStr = "get USERNAME"
|
||||
)
|
||||
|
||||
// GetOptions is an options struct to support get subcommands.
|
||||
type GetOptions struct {
|
||||
Name string
|
||||
|
||||
iamclient iam.IamInterface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
getExample = templates.Examples(`
|
||||
# Get user foo detail information
|
||||
iamctl user get foo`)
|
||||
|
||||
getUsageErrStr = fmt.Sprintf("expected '%s'.\nUSERNAME is required arguments for the get command", getUsageStr)
|
||||
)
|
||||
|
||||
// NewGetOptions returns an initialized GetOptions instance.
|
||||
func NewGetOptions(ioStreams genericclioptions.IOStreams) *GetOptions {
|
||||
return &GetOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdGet returns new initialized instance of get sub command.
|
||||
func NewCmdGet(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewGetOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: getUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Display a user resource.",
|
||||
TraverseChildren: true,
|
||||
Long: `Display a user resource.`,
|
||||
Example: getExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, getUsageErrStr)
|
||||
}
|
||||
|
||||
o.Name = args[0]
|
||||
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *GetOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a get subcommand using the specified options.
|
||||
func (o *GetOptions) Run(args []string) error {
|
||||
user, err := o.iamclient.APIV1().Users().Get(context.TODO(), o.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
table := tablewriter.NewWriter(o.Out)
|
||||
|
||||
data := [][]string{
|
||||
{
|
||||
user.Name,
|
||||
user.Nickname,
|
||||
user.Email,
|
||||
user.Phone,
|
||||
user.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
user.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
}
|
||||
|
||||
table = setHeader(table)
|
||||
table = cmdutil.TableWriterDefaultConfig(table)
|
||||
table.AppendBulk(data)
|
||||
table.Render()
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLimit = 1000
|
||||
)
|
||||
|
||||
// ListOptions is an options struct to support list subcommands.
|
||||
type ListOptions struct {
|
||||
Offset int64
|
||||
Limit int64
|
||||
|
||||
iamclient iam.IamInterface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var listExample = templates.Examples(`
|
||||
# List all users
|
||||
iamctl user list
|
||||
|
||||
# List users with limit and offset
|
||||
iamctl user list --offset=0 --limit=10`)
|
||||
|
||||
// NewListOptions returns an initialized ListOptions instance.
|
||||
func NewListOptions(ioStreams genericclioptions.IOStreams) *ListOptions {
|
||||
return &ListOptions{
|
||||
IOStreams: ioStreams,
|
||||
Offset: 0,
|
||||
Limit: defaultLimit,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdList returns new initialized instance of list sub command.
|
||||
func NewCmdList(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewListOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Display all users in iam platform (Administrator rights required)",
|
||||
TraverseChildren: true,
|
||||
Long: "Display all users in iam platform (Administrator rights required).",
|
||||
Example: listExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
cmd.Flags().Int64VarP(&o.Offset, "offset", "o", o.Offset, "Specify the offset of the first row to be returned.")
|
||||
cmd.Flags().Int64VarP(&o.Limit, "limit", "l", o.Limit, "Specify the amount records to be returned.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *ListOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *ListOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a list subcommand using the specified options.
|
||||
func (o *ListOptions) Run(args []string) error {
|
||||
users, err := o.iamclient.APIV1().Users().List(context.TODO(), metav1.ListOptions{
|
||||
Offset: &o.Offset,
|
||||
Limit: &o.Limit,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make([][]string, 0, 1)
|
||||
table := tablewriter.NewWriter(o.Out)
|
||||
|
||||
for _, user := range users.Items {
|
||||
data = append(data, []string{
|
||||
user.Name, user.Nickname, user.Email,
|
||||
user.Phone, user.CreatedAt.Format("2006-01-02 15:04:05"), user.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
table = setHeader(table)
|
||||
table = cmdutil.TableWriterDefaultConfig(table)
|
||||
table.AppendBulk(data)
|
||||
table.Render()
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
metav1 "github.com/openim-sigs/component-base/pkg/meta/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
updateUsageStr = "update USERNAME"
|
||||
)
|
||||
|
||||
// UpdateOptions is an options struct to support update subcommands.
|
||||
type UpdateOptions struct {
|
||||
Name string
|
||||
Nickname string
|
||||
Email string
|
||||
Phone string
|
||||
|
||||
iamclient iam.IamInterface
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
updateLong = templates.LongDesc(`Update a user resource.
|
||||
|
||||
Can only update nickname, email and phone.
|
||||
|
||||
NOTICE: field will be updated to zero value if not specified.`)
|
||||
|
||||
updateExample = templates.Examples(`
|
||||
# Update use foo's information
|
||||
iamctl user update foo --nickname=foo2 --email=foo@qq.com --phone=1812883xxxx`)
|
||||
|
||||
updateUsageErrStr = fmt.Sprintf(
|
||||
"expected '%s'.\nUSERNAME is required arguments for the update command",
|
||||
updateUsageStr,
|
||||
)
|
||||
)
|
||||
|
||||
// NewUpdateOptions returns an initialized UpdateOptions instance.
|
||||
func NewUpdateOptions(ioStreams genericclioptions.IOStreams) *UpdateOptions {
|
||||
return &UpdateOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdUpdate returns new initialized instance of update sub command.
|
||||
func NewCmdUpdate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewUpdateOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: updateUsageStr,
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Update a user resource",
|
||||
TraverseChildren: true,
|
||||
Long: updateLong,
|
||||
Example: updateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&o.Nickname, "nickname", o.Nickname, "The nickname of the user.")
|
||||
cmd.Flags().StringVar(&o.Email, "email", o.Email, "The email of the user.")
|
||||
cmd.Flags().StringVar(&o.Phone, "phone", o.Phone, "The phone number of the user.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *UpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, updateUsageErrStr)
|
||||
}
|
||||
|
||||
o.Name = args[0]
|
||||
o.iamclient, err = f.IAMClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *UpdateOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes an update subcommand using the specified options.
|
||||
func (o *UpdateOptions) Run(args []string) error {
|
||||
user, err := o.iamclient.APIV1().Users().Get(context.TODO(), o.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.Nickname != "" {
|
||||
user.Nickname = o.Nickname
|
||||
}
|
||||
if o.Email != "" {
|
||||
user.Email = o.Email
|
||||
}
|
||||
if o.Phone != "" {
|
||||
user.Phone = o.Phone
|
||||
}
|
||||
|
||||
ret, err := o.iamclient.APIV1().Users().Update(context.TODO(), user, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "user/%s updated\n", ret.Name)
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
restclient "github.com/marmotedu/marmotedu-sdk-go/rest"
|
||||
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
// Factory provides abstractions that allow the IAM command to be extended across multiple types
|
||||
// of resources and different API sets.
|
||||
// The rings are here for a reason. In order for composers to be able to provide alternative factory implementations
|
||||
// they need to provide low level pieces of *certain* functions so that when the factory calls back into itself
|
||||
// it uses the custom version of the function. Rather than try to enumerate everything that someone would want to
|
||||
// override
|
||||
// we split the factory into rings, where each ring can depend on methods in an earlier ring, but cannot depend
|
||||
// upon peer methods in its own ring.
|
||||
// TODO: make the functions interfaces
|
||||
// TODO: pass the various interfaces on the factory directly into the command constructors (so the
|
||||
// commands are decoupled from the factory).
|
||||
type Factory interface {
|
||||
genericclioptions.RESTClientGetter
|
||||
|
||||
// IAMClient gives you back an external iamclient
|
||||
IAMClient() (*iam.IamClient, error)
|
||||
|
||||
// Returns a RESTClient for accessing IAM resources or an error.
|
||||
RESTClient() (*restclient.RESTClient, error)
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// this file contains factories with no other dependencies
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu/service/iam"
|
||||
restclient "github.com/marmotedu/marmotedu-sdk-go/rest"
|
||||
"github.com/marmotedu/marmotedu-sdk-go/tools/clientcmd"
|
||||
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
type factoryImpl struct {
|
||||
clientGetter genericclioptions.RESTClientGetter
|
||||
}
|
||||
|
||||
func NewFactory(clientGetter genericclioptions.RESTClientGetter) Factory {
|
||||
if clientGetter == nil {
|
||||
panic("attempt to instantiate client_access_factory with nil clientGetter")
|
||||
}
|
||||
|
||||
f := &factoryImpl{
|
||||
clientGetter: clientGetter,
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *factoryImpl) ToRESTConfig() (*restclient.Config, error) {
|
||||
return f.clientGetter.ToRESTConfig()
|
||||
}
|
||||
|
||||
func (f *factoryImpl) ToRawIAMConfigLoader() clientcmd.ClientConfig {
|
||||
return f.clientGetter.ToRawIAMConfigLoader()
|
||||
}
|
||||
|
||||
func (f *factoryImpl) IAMClient() (*iam.IamClient, error) {
|
||||
clientConfig, err := f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iam.NewForConfig(clientConfig)
|
||||
}
|
||||
|
||||
func (f *factoryImpl) RESTClient() (*restclient.RESTClient, error) {
|
||||
clientConfig, err := f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setIAMDefaults(clientConfig)
|
||||
return restclient.RESTClientFor(clientConfig)
|
||||
}
|
@ -0,0 +1,397 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/marmotedu/errors"
|
||||
"github.com/marmotedu/marmotedu-sdk-go/marmotedu"
|
||||
restclient "github.com/marmotedu/marmotedu-sdk-go/rest"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/marmotedu/iam/pkg/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultErrorExitCode defines the default exit code.
|
||||
DefaultErrorExitCode = 1
|
||||
)
|
||||
|
||||
type debugError interface {
|
||||
DebugError() (msg string, args []interface{})
|
||||
}
|
||||
|
||||
var fatalErrHandler = fatal
|
||||
|
||||
// BehaviorOnFatal allows you to override the default behavior when a fatal
|
||||
// error occurs, which is to call os.Exit(code). You can pass 'panic' as a function
|
||||
// here if you prefer the panic() over os.Exit(1).
|
||||
func BehaviorOnFatal(f func(string, int)) {
|
||||
fatalErrHandler = f
|
||||
}
|
||||
|
||||
// DefaultBehaviorOnFatal allows you to undo any previous override. Useful in
|
||||
// tests.
|
||||
func DefaultBehaviorOnFatal() {
|
||||
fatalErrHandler = fatal
|
||||
}
|
||||
|
||||
// fatal prints the message (if provided) and then exits.
|
||||
func fatal(msg string, code int) {
|
||||
if len(msg) > 0 {
|
||||
// add newline if needed
|
||||
if !strings.HasSuffix(msg, "\n") {
|
||||
msg += "\n"
|
||||
}
|
||||
fmt.Fprint(os.Stderr, msg)
|
||||
}
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// ErrExit may be passed to CheckError to instruct it to output nothing but exit with
|
||||
// status code 1.
|
||||
var ErrExit = fmt.Errorf("exit")
|
||||
|
||||
// CheckErr prints a user-friendly error to STDERR and exits with a non-zero
|
||||
// exit code. Unrecognized errors will be printed with an "error: " prefix.
|
||||
//
|
||||
// This method is generic to the command in use and may be used by non-IAM
|
||||
// commands.
|
||||
func CheckErr(err error) {
|
||||
checkErr(err, fatalErrHandler)
|
||||
}
|
||||
|
||||
// CheckDiffErr prints a user-friendly error to STDERR and exits with a
|
||||
// non-zero and non-one exit code. Unrecognized errors will be printed
|
||||
// with an "error: " prefix.
|
||||
//
|
||||
// This method is meant specifically for `iamctl diff` and may be used
|
||||
// by other commands.
|
||||
func CheckDiffErr(err error) {
|
||||
checkErr(err, func(msg string, code int) {
|
||||
fatalErrHandler(msg, code+1)
|
||||
})
|
||||
}
|
||||
|
||||
// checkErr formats a given error as a string and calls the passed handleErr
|
||||
// func with that string and an iamctl exit code.
|
||||
func checkErr(err error, handleErr func(string, int)) {
|
||||
// unwrap aggregates of 1
|
||||
if agg, ok := err.(errors.Aggregate); ok && len(agg.Errors()) == 1 {
|
||||
err = agg.Errors()[0]
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case err == ErrExit:
|
||||
handleErr("", DefaultErrorExitCode)
|
||||
default:
|
||||
switch err := err.(type) {
|
||||
case errors.Aggregate:
|
||||
handleErr(MultipleErrors(``, err.Errors()), DefaultErrorExitCode)
|
||||
default: // for any other error type
|
||||
msg, ok := StandardErrorMessage(err)
|
||||
if !ok {
|
||||
msg = err.Error()
|
||||
if !strings.HasPrefix(msg, "error: ") {
|
||||
msg = fmt.Sprintf("error: %s", msg)
|
||||
}
|
||||
}
|
||||
handleErr(msg, DefaultErrorExitCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StandardErrorMessage translates common errors into a human readable message, or returns
|
||||
// false if the error is not one of the recognized types. It may also log extended information to klog.
|
||||
//
|
||||
// This method is generic to the command in use and may be used by non-IAM
|
||||
// commands.
|
||||
func StandardErrorMessage(err error) (string, bool) {
|
||||
if debugErr, ok := err.(debugError); ok {
|
||||
log.Infof(debugErr.DebugError())
|
||||
}
|
||||
if t, ok := err.(*url.Error); ok {
|
||||
log.Infof("Connection error: %s %s: %v", t.Op, t.URL, t.Err)
|
||||
if strings.Contains(t.Err.Error(), "connection refused") {
|
||||
host := t.URL
|
||||
if server, err := url.Parse(t.URL); err == nil {
|
||||
host = server.Host
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"The connection to the server %s was refused - did you specify the right host or port?",
|
||||
host,
|
||||
), true
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Unable to connect to the server: %v", t.Err), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// MultilineError returns a string representing an error that splits sub errors into their own
|
||||
// lines. The returned string will end with a newline.
|
||||
func MultilineError(prefix string, err error) string {
|
||||
if agg, ok := err.(errors.Aggregate); ok {
|
||||
errs := errors.Flatten(agg).Errors()
|
||||
buf := &bytes.Buffer{}
|
||||
switch len(errs) {
|
||||
case 0:
|
||||
return fmt.Sprintf("%s%v\n", prefix, err)
|
||||
case 1:
|
||||
return fmt.Sprintf("%s%v\n", prefix, messageForError(errs[0]))
|
||||
default:
|
||||
fmt.Fprintln(buf, prefix)
|
||||
for _, err := range errs {
|
||||
fmt.Fprintf(buf, "* %v\n", messageForError(err))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s%s\n", prefix, err)
|
||||
}
|
||||
|
||||
// MultipleErrors returns a newline delimited string containing
|
||||
// the prefix and referenced errors in standard form.
|
||||
func MultipleErrors(prefix string, errs []error) string {
|
||||
buf := &bytes.Buffer{}
|
||||
for _, err := range errs {
|
||||
fmt.Fprintf(buf, "%s%v\n", prefix, messageForError(err))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// messageForError returns the string representing the error.
|
||||
func messageForError(err error) string {
|
||||
msg, ok := StandardErrorMessage(err)
|
||||
if !ok {
|
||||
msg = err.Error()
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// UsageErrorf returns error with command path.
|
||||
func UsageErrorf(cmd *cobra.Command, format string, args ...interface{}) error {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
return fmt.Errorf("%s\nSee '%s -h' for help and examples", msg, cmd.CommandPath())
|
||||
}
|
||||
|
||||
// IsFilenameSliceEmpty checkes where filenames and directory are both zero value.
|
||||
func IsFilenameSliceEmpty(filenames []string, directory string) bool {
|
||||
return len(filenames) == 0 && directory == ""
|
||||
}
|
||||
|
||||
// GetFlagString returns the value of the given flag.
|
||||
func GetFlagString(cmd *cobra.Command, flag string) string {
|
||||
s, err := cmd.Flags().GetString(flag)
|
||||
if err != nil {
|
||||
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetFlagStringSlice can be used to accept multiple argument with flag repetition (e.g. -f arg1,arg2 -f arg3 ...).
|
||||
func GetFlagStringSlice(cmd *cobra.Command, flag string) []string {
|
||||
s, err := cmd.Flags().GetStringSlice(flag)
|
||||
if err != nil {
|
||||
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetFlagStringArray can be used to accept multiple argument with flag repetition (e.g. -f arg1 -f arg2 ...).
|
||||
func GetFlagStringArray(cmd *cobra.Command, flag string) []string {
|
||||
s, err := cmd.Flags().GetStringArray(flag)
|
||||
if err != nil {
|
||||
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetFlagBool returns the value of the given flag.
|
||||
func GetFlagBool(cmd *cobra.Command, flag string) bool {
|
||||
b, err := cmd.Flags().GetBool(flag)
|
||||
if err != nil {
|
||||
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// GetFlagInt returns the value of the given flag.
|
||||
func GetFlagInt(cmd *cobra.Command, flag string) int {
|
||||
i, err := cmd.Flags().GetInt(flag)
|
||||
if err != nil {
|
||||
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// GetFlagInt32 returns the value of the given flag.
|
||||
func GetFlagInt32(cmd *cobra.Command, flag string) int32 {
|
||||
i, err := cmd.Flags().GetInt32(flag)
|
||||
if err != nil {
|
||||
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// GetFlagInt64 returns the value of the given flag.
|
||||
func GetFlagInt64(cmd *cobra.Command, flag string) int64 {
|
||||
i, err := cmd.Flags().GetInt64(flag)
|
||||
if err != nil {
|
||||
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// GetFlagDuration return the value of the given flag.
|
||||
func GetFlagDuration(cmd *cobra.Command, flag string) time.Duration {
|
||||
d, err := cmd.Flags().GetDuration(flag)
|
||||
if err != nil {
|
||||
log.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// AddCleanFlags add clean flags.
|
||||
func AddCleanFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().StringP("user", "u", "", "Specify the user name.")
|
||||
cmd.Flags().BoolP("erase", "c", false, "Erase the records from the db")
|
||||
}
|
||||
|
||||
// ValidateOptions defines the validate options.
|
||||
type ValidateOptions struct {
|
||||
EnableValidation bool
|
||||
}
|
||||
|
||||
// IsSiblingCommandExists receives a pointer to a cobra command and a target string.
|
||||
// Returns true if the target string is found in the list of sibling commands.
|
||||
func IsSiblingCommandExists(cmd *cobra.Command, targetCmdName string) bool {
|
||||
for _, c := range cmd.Parent().Commands() {
|
||||
if c.Name() == targetCmdName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultSubCommandRun prints a command's help string to the specified output if no
|
||||
// arguments (sub-commands) are provided, or a usage error otherwise.
|
||||
func DefaultSubCommandRun(out io.Writer) func(c *cobra.Command, args []string) {
|
||||
return func(c *cobra.Command, args []string) {
|
||||
c.SetOutput(out)
|
||||
RequireNoArguments(c, args)
|
||||
c.Help()
|
||||
CheckErr(ErrExit)
|
||||
}
|
||||
}
|
||||
|
||||
// RequireNoArguments exits with a usage error if extra arguments are provided.
|
||||
func RequireNoArguments(c *cobra.Command, args []string) {
|
||||
if len(args) > 0 {
|
||||
CheckErr(UsageErrorf(c, "unknown command %q", strings.Join(args, " ")))
|
||||
}
|
||||
}
|
||||
|
||||
// ManualStrip is used for dropping comments from a YAML file.
|
||||
func ManualStrip(file []byte) []byte {
|
||||
stripped := []byte{}
|
||||
lines := bytes.Split(file, []byte("\n"))
|
||||
for i, line := range lines {
|
||||
if bytes.HasPrefix(bytes.TrimSpace(line), []byte("#")) {
|
||||
continue
|
||||
}
|
||||
stripped = append(stripped, line...)
|
||||
if i < len(lines)-1 {
|
||||
stripped = append(stripped, '\n')
|
||||
}
|
||||
}
|
||||
return stripped
|
||||
}
|
||||
|
||||
// Warning write warning message to io.Writer.
|
||||
func Warning(cmdErr io.Writer, newGeneratorName, oldGeneratorName string) {
|
||||
fmt.Fprintf(cmdErr, "WARNING: New generator %q specified, "+
|
||||
"but it isn't available. "+
|
||||
"Falling back to %q.\n",
|
||||
newGeneratorName,
|
||||
oldGeneratorName,
|
||||
)
|
||||
}
|
||||
|
||||
// CombineRequestErr combines the http response error and error in errs array.
|
||||
func CombineRequestErr(resp gorequest.Response, body string, errs []error) error {
|
||||
var e, sep string
|
||||
if len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
e = sep + err.Error()
|
||||
sep = "\n"
|
||||
}
|
||||
return errors.New(e)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(body)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewForConfigOrDie() *marmotedu.Clientset {
|
||||
clientConfig := &restclient.Config{
|
||||
Host: viper.GetString("server.address"),
|
||||
BearerToken: viper.GetString("user.token"),
|
||||
Username: viper.GetString("user.username"),
|
||||
Password: viper.GetString("user.password"),
|
||||
SecretID: viper.GetString("user.secret-id"),
|
||||
SecretKey: viper.GetString("user.secret-key"),
|
||||
Timeout: viper.GetDuration("server.timeout"),
|
||||
MaxRetries: viper.GetInt("server.max-retries"),
|
||||
RetryInterval: viper.GetDuration("server.retry-interval"),
|
||||
}
|
||||
|
||||
return marmotedu.NewForConfigOrDie(clientConfig)
|
||||
}
|
||||
|
||||
func TableWriterDefaultConfig(table *tablewriter.Table) *tablewriter.Table {
|
||||
table.SetAutoWrapText(false)
|
||||
table.SetAutoFormatHeaders(true)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetCenterSeparator("")
|
||||
table.SetColumnSeparator("")
|
||||
table.SetRowSeparator("")
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetTablePadding(" ") // pad with two space
|
||||
table.SetNoWhiteSpace(true)
|
||||
|
||||
return table
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/marmotedu/marmotedu-sdk-go/rest"
|
||||
"github.com/marmotedu/marmotedu-sdk-go/tools/clientcmd"
|
||||
"github.com/openim-sigs/component-base/pkg/runtime"
|
||||
"github.com/openim-sigs/component-base/pkg/scheme"
|
||||
"github.com/openim-sigs/component-base/pkg/version"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
flagMatchBinaryVersion = "match-server-version"
|
||||
)
|
||||
|
||||
// MatchVersionFlags is for setting the "match server version" function.
|
||||
type MatchVersionFlags struct {
|
||||
Delegate genericclioptions.RESTClientGetter
|
||||
|
||||
RequireMatchedServerVersion bool
|
||||
checkServerVersion sync.Once
|
||||
matchesServerVersionErr error
|
||||
}
|
||||
|
||||
var _ genericclioptions.RESTClientGetter = &MatchVersionFlags{}
|
||||
|
||||
func (f *MatchVersionFlags) checkMatchingServerVersion() error {
|
||||
f.checkServerVersion.Do(func() {
|
||||
if !f.RequireMatchedServerVersion {
|
||||
return
|
||||
}
|
||||
|
||||
clientConfig, err := f.Delegate.ToRESTConfig()
|
||||
if err != nil {
|
||||
f.matchesServerVersionErr = err
|
||||
return
|
||||
}
|
||||
|
||||
setIAMDefaults(clientConfig)
|
||||
restClient, err := rest.RESTClientFor(clientConfig)
|
||||
if err != nil {
|
||||
f.matchesServerVersionErr = err
|
||||
return
|
||||
}
|
||||
|
||||
var sVer *version.Info
|
||||
if err := restClient.Get().AbsPath("/version").Do(context.TODO()).Into(&sVer); err != nil {
|
||||
f.matchesServerVersionErr = err
|
||||
return
|
||||
}
|
||||
|
||||
clientVersion := version.Get()
|
||||
|
||||
// GitVersion includes GitCommit and GitTreeState, but best to be safe?
|
||||
if clientVersion.GitVersion != sVer.GitVersion || clientVersion.GitCommit != sVer.GitCommit ||
|
||||
clientVersion.GitTreeState != sVer.GitTreeState {
|
||||
f.matchesServerVersionErr = fmt.Errorf(
|
||||
"server version (%#v) differs from client version (%#v)",
|
||||
sVer,
|
||||
version.Get(),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return f.matchesServerVersionErr
|
||||
}
|
||||
|
||||
// ToRESTConfig implements RESTClientGetter.
|
||||
// Returns a REST client configuration based on a provided path
|
||||
// to a .iamconfig file, loading rules, and config flag overrides.
|
||||
// Expects the AddFlags method to have been called.
|
||||
func (f *MatchVersionFlags) ToRESTConfig() (*rest.Config, error) {
|
||||
if err := f.checkMatchingServerVersion(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientConfig, err := f.Delegate.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO we should not have to do this. It smacks of something going wrong.
|
||||
setIAMDefaults(clientConfig)
|
||||
return clientConfig, nil
|
||||
}
|
||||
|
||||
func (f *MatchVersionFlags) ToRawIAMConfigLoader() clientcmd.ClientConfig {
|
||||
return f.Delegate.ToRawIAMConfigLoader()
|
||||
}
|
||||
|
||||
func (f *MatchVersionFlags) AddFlags(flags *pflag.FlagSet) {
|
||||
flags.BoolVar(
|
||||
&f.RequireMatchedServerVersion,
|
||||
flagMatchBinaryVersion,
|
||||
f.RequireMatchedServerVersion,
|
||||
"Require server version to match client version",
|
||||
)
|
||||
}
|
||||
|
||||
func NewMatchVersionFlags(delegate genericclioptions.RESTClientGetter) *MatchVersionFlags {
|
||||
return &MatchVersionFlags{
|
||||
Delegate: delegate,
|
||||
}
|
||||
}
|
||||
|
||||
// setIAMDefaults sets default values on the provided client config for accessing the
|
||||
// IAM API or returns an error if any of the defaults are impossible or invalid.
|
||||
// TODO this isn't what we want. Each iamclient should be setting defaults as it sees fit.
|
||||
func setIAMDefaults(config *rest.Config) error {
|
||||
// TODO remove this hack. This is allowing the GetOptions to be serialized.
|
||||
config.GroupVersion = &scheme.GroupVersion{Group: "iam.api", Version: "v1"}
|
||||
|
||||
if config.APIPath == "" {
|
||||
config.APIPath = "/api"
|
||||
}
|
||||
if config.Negotiator == nil {
|
||||
// This codec factory ensures the resources are not converted. Therefore, resources
|
||||
// will not be round-tripped through internal versions. Defaulting does not happen
|
||||
// on the client.
|
||||
config.Negotiator = runtime.NewSimpleClientNegotiator()
|
||||
}
|
||||
return rest.SetIAMDefaults(config)
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package validate validate the basic environment for iamctl to run.
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
// ValidateOptions is an options struct to support 'validate' sub command.
|
||||
type ValidateOptions struct {
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
// ValidateInfo defines the validate information.
|
||||
type ValidateInfo struct {
|
||||
ItemName string
|
||||
Status string
|
||||
Message string
|
||||
}
|
||||
|
||||
var validateExample = templates.Examples(`
|
||||
# Validate the basic environment for iamctl to run
|
||||
iamctl validate`)
|
||||
|
||||
// NewValidateOptions returns an initialized ValidateOptions instance.
|
||||
func NewValidateOptions(ioStreams genericclioptions.IOStreams) *ValidateOptions {
|
||||
return &ValidateOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdValidate returns new initialized instance of 'validate' sub command.
|
||||
func NewCmdValidate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewValidateOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "validate",
|
||||
DisableFlagsInUseLine: true,
|
||||
Aliases: []string{},
|
||||
Short: "Validate the basic environment for iamctl to run",
|
||||
TraverseChildren: true,
|
||||
Long: "Validate the basic environment for iamctl to run.",
|
||||
Example: validateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd, args))
|
||||
cmdutil.CheckErr(o.Run(args))
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *ValidateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate makes sure there is no discrepency in command options.
|
||||
func (o *ValidateOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes a validate sub command using the specified options.
|
||||
func (o *ValidateOptions) Run(args []string) error {
|
||||
data := [][]string{}
|
||||
FAIL := color.RedString("FAIL")
|
||||
PASS := color.GreenString("PASS")
|
||||
validateInfo := ValidateInfo{}
|
||||
|
||||
// check if can access db
|
||||
validateInfo.ItemName = "iam-apiserver"
|
||||
target, err := url.Parse(viper.GetString("server.address"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = net.Dial("tcp", target.Host)
|
||||
// defer client.Close()
|
||||
if err != nil {
|
||||
validateInfo.Status = FAIL
|
||||
validateInfo.Message = fmt.Sprintf("%v", err)
|
||||
} else {
|
||||
validateInfo.Status = PASS
|
||||
validateInfo.Message = ""
|
||||
}
|
||||
|
||||
data = append(data, []string{validateInfo.ItemName, validateInfo.Status, validateInfo.Message})
|
||||
|
||||
table := tablewriter.NewWriter(o.Out)
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetColWidth(iamctl.TableWidth)
|
||||
table.SetHeader([]string{"ValidateItem", "Result", "Message"})
|
||||
|
||||
for _, v := range data {
|
||||
table.Append(v)
|
||||
}
|
||||
|
||||
table.Render()
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package version print the client and server version information.
|
||||
package version
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
restclient "github.com/marmotedu/marmotedu-sdk-go/rest"
|
||||
"github.com/openim-sigs/component-base/pkg/json"
|
||||
"github.com/openim-sigs/component-base/pkg/version"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/util"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
|
||||
// Version is a struct for version information.
|
||||
type Version struct {
|
||||
ClientVersion *version.Info `json:"clientVersion,omitempty" yaml:"clientVersion,omitempty"`
|
||||
ServerVersion *version.Info `json:"serverVersion,omitempty" yaml:"serverVersion,omitempty"`
|
||||
}
|
||||
|
||||
var versionExample = templates.Examples(`
|
||||
# Print the client and server versions for the current context
|
||||
iamctl version`)
|
||||
|
||||
// Options is a struct to support version command.
|
||||
type Options struct {
|
||||
ClientOnly bool
|
||||
Short bool
|
||||
Output string
|
||||
|
||||
client *restclient.RESTClient
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
// NewOptions returns initialized Options.
|
||||
func NewOptions(ioStreams genericclioptions.IOStreams) *Options {
|
||||
return &Options{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCmdVersion returns a cobra command for fetching versions.
|
||||
func NewCmdVersion(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewOptions(ioStreams)
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the client and server version information",
|
||||
Long: "Print the client and server version information for the current context",
|
||||
Example: versionExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(
|
||||
&o.ClientOnly,
|
||||
"client",
|
||||
o.ClientOnly,
|
||||
"If true, shows client version only (no server required).",
|
||||
)
|
||||
cmd.Flags().BoolVar(&o.Short, "short", o.Short, "If true, print just the version number.")
|
||||
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "One of 'yaml' or 'json'.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes all the required options.
|
||||
func (o *Options) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
|
||||
var err error
|
||||
if o.ClientOnly {
|
||||
return nil
|
||||
}
|
||||
|
||||
o.client, err = f.RESTClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the provided options.
|
||||
func (o *Options) Validate() error {
|
||||
if o.Output != "" && o.Output != "yaml" && o.Output != "json" {
|
||||
return errors.New(`--output must be 'yaml' or 'json'`)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes version command.
|
||||
func (o *Options) Run() error {
|
||||
var (
|
||||
serverVersion *version.Info
|
||||
serverErr error
|
||||
versionInfo Version
|
||||
)
|
||||
|
||||
clientVersion := version.Get()
|
||||
versionInfo.ClientVersion = &clientVersion
|
||||
|
||||
if !o.ClientOnly && o.client != nil {
|
||||
// Always request fresh data from the server
|
||||
if err := o.client.Get().AbsPath("/version").Do(context.TODO()).Into(&serverVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
versionInfo.ServerVersion = serverVersion
|
||||
}
|
||||
|
||||
switch o.Output {
|
||||
case "":
|
||||
if o.Short {
|
||||
fmt.Fprintf(o.Out, "Client Version: %s\n", clientVersion.GitVersion)
|
||||
|
||||
if serverVersion != nil {
|
||||
fmt.Fprintf(o.Out, "Server Version: %s\n", serverVersion.GitVersion)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(o.Out, "Client Version: %s\n", fmt.Sprintf("%#v", clientVersion))
|
||||
if serverVersion != nil {
|
||||
fmt.Fprintf(o.Out, "Server Version: %s\n", fmt.Sprintf("%#v", *serverVersion))
|
||||
}
|
||||
}
|
||||
case "yaml":
|
||||
marshaled, err := yaml.Marshal(&versionInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(o.Out, string(marshaled))
|
||||
case "json":
|
||||
marshaled, err := json.MarshalIndent(&versionInfo, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(o.Out, string(marshaled))
|
||||
default:
|
||||
// There is a bug in the program if we hit this case.
|
||||
// However, we follow a policy of never panicking.
|
||||
return fmt.Errorf("VersionOptions were not validated: --output=%q should have been rejected", o.Output)
|
||||
}
|
||||
|
||||
return serverErr
|
||||
}
|
Loading…
Reference in new issue