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