Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com>pull/926/head
parent
0a27b6e0a6
commit
a409045e99
@ -1,89 +0,0 @@
|
|||||||
# [RFC #0005] OpenIM CTL Module Proposal
|
|
||||||
|
|
||||||
## Meta
|
|
||||||
|
|
||||||
- Name: OpenIM CTL Module Enhancement
|
|
||||||
- Start Date: 2023-08-23
|
|
||||||
- Author(s): @cubxxw
|
|
||||||
- Status: Draft
|
|
||||||
- RFC Pull Request: (leave blank)
|
|
||||||
- OpenIMSDK Pull Request: (leave blank)
|
|
||||||
- OpenIMSDK Issue: https://github.com/OpenIMSDK/Open-IM-Server/issues/924
|
|
||||||
- Supersedes: N/A
|
|
||||||
|
|
||||||
## 📇Topics
|
|
||||||
|
|
||||||
- RFC #0000 OpenIMSDK CTL Module Proposal
|
|
||||||
- [Meta](#meta)
|
|
||||||
- [Summary](#summary)
|
|
||||||
- [Definitions](#definitions)
|
|
||||||
- [Motivation](#motivation)
|
|
||||||
- [What it is](#what-it-is)
|
|
||||||
- [How it Works](#how-it-works)
|
|
||||||
- [Migration](#migration)
|
|
||||||
- [Drawbacks](#drawbacks)
|
|
||||||
- [Alternatives](#alternatives)
|
|
||||||
- [Prior Art](#prior-art)
|
|
||||||
- [Unresolved Questions](#unresolved-questions)
|
|
||||||
- [Spec. Changes (OPTIONAL)](#spec-changes-optional)
|
|
||||||
- [History](#history)
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
The OpenIM CTL module proposal aims to provide an integrated tool for the OpenIM system, offering utilities for user management, system monitoring, debugging, configuration, and more. This tool will enhance the extensibility of the OpenIM system and reduce dependencies on individual modules.
|
|
||||||
|
|
||||||
## Definitions
|
|
||||||
|
|
||||||
- **OpenIM**: An Instant Messaging system.
|
|
||||||
- **`imctl`**: The control command-line tool for OpenIM.
|
|
||||||
- **E2E Testing**: End-to-End Testing.
|
|
||||||
- **API**: Application Programming Interface.
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
- Improve the OpenIM system's extensibility and reduce dependencies on individual modules.
|
|
||||||
- Simplify the process for testers to perform automated tests.
|
|
||||||
- Enhance interaction with scripts and reduce the system's coupling.
|
|
||||||
- Implement a consistent tool similar to kubectl for a streamlined user experience.
|
|
||||||
|
|
||||||
## What it is
|
|
||||||
|
|
||||||
`imctl` is a command-line utility designed for OpenIM to provide functionalities including:
|
|
||||||
|
|
||||||
- User Management: Add, delete, or disable user accounts.
|
|
||||||
- System Monitoring: View metrics like online users, message transfer rate.
|
|
||||||
- Debugging: View logs, adjust log levels, check system states.
|
|
||||||
- Configuration Management: Update system settings, manage plugins/modules.
|
|
||||||
- Data Management: Backup, restore, import, or export data.
|
|
||||||
- System Maintenance: Update, restart services, or maintenance mode.
|
|
||||||
|
|
||||||
## How it Works
|
|
||||||
|
|
||||||
`imctl`, inspired by kubectl, will have sub-commands and options for the functionalities mentioned. Developers, operations, and testers can invoke these commands to manage and monitor the OpenIM system.
|
|
||||||
|
|
||||||
## Migration
|
|
||||||
|
|
||||||
Currently, the `imctl` will be housed in `tools/imctl`, and later on, the plan is to move it to `cmd/imctl`. Migration guidelines will be provided to ensure smooth transitions.
|
|
||||||
|
|
||||||
## Drawbacks
|
|
||||||
|
|
||||||
- Overhead in learning and adapting to a new tool for existing users.
|
|
||||||
- Potential complexities in implementing some of the advanced functionalities.
|
|
||||||
|
|
||||||
## Alternatives
|
|
||||||
|
|
||||||
- Continue using individual modules for OpenIM management.
|
|
||||||
- Utilize third-party tools or platforms with similar functionalities, customizing them for OpenIM.
|
|
||||||
|
|
||||||
## Prior Art
|
|
||||||
|
|
||||||
Kubectl from Kubernetes is a significant inspiration for `imctl`, offering a comprehensive command-line tool for managing clusters.
|
|
||||||
|
|
||||||
## Unresolved Questions
|
|
||||||
|
|
||||||
- What other functionalities might be required in future versions of `imctl`?
|
|
||||||
- What's the expected timeline for transitioning from `tools/imctl` to `cmd/imctl`?
|
|
||||||
|
|
||||||
## Spec. Changes (OPTIONAL)
|
|
||||||
|
|
||||||
As of now, there are no proposed changes to the core specifications or extensions. Future changes based on community feedback might necessitate spec changes, which will be documented accordingly.
|
|
@ -1,49 +0,0 @@
|
|||||||
# OpenIM `man` Module README
|
|
||||||
|
|
||||||
Welcome to the `man` module of OpenIM, the comprehensive guide for using OpenIM's range of powerful commands. Here, you'll find in-depth details for each command, its options, and examples to help you harness the full power of the OpenIM suite.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
OpenIM is a robust instant messaging solution. To ensure users can effectively harness its capabilities, OpenIM provides a suite of commands that serve different functionalities, from the API level to RPC calls and utilities.
|
|
||||||
|
|
||||||
The `man` module ensures that users, both new and experienced, have a reliable source of information and documentation to use these commands effectively.
|
|
||||||
|
|
||||||
## Available Commands
|
|
||||||
|
|
||||||
The OpenIM commands are divided into core services and tools. Below is a brief overview of each:
|
|
||||||
|
|
||||||
### Core Services
|
|
||||||
|
|
||||||
- **openim-api**: Interface to the main functionalities of OpenIM.
|
|
||||||
- **openim-cmdutils**: Utilities for executing common tasks.
|
|
||||||
- **openim-crontask**: Schedule and manage routine tasks within OpenIM.
|
|
||||||
- **openim-msggateway**: Gateway for managing messages within the OpenIM system.
|
|
||||||
- **openim-msgtransfer**: Handle message transfers across different parts of OpenIM.
|
|
||||||
- **openim-push**: Service for pushing notifications and updates.
|
|
||||||
- **openim-rpc-auth**: RPC interface for authentication tasks.
|
|
||||||
- **openim-rpc-conversation**: RPC service for handling conversations.
|
|
||||||
- **openim-rpc-friend**: Manage friend lists and related functionalities through RPC.
|
|
||||||
- **openim-rpc-group**: Group management via RPC.
|
|
||||||
- **openim-rpc-msg**: Message handling at the RPC level.
|
|
||||||
- **openim-rpc-third**: Third-party integrations and related tasks through RPC.
|
|
||||||
- **openim-rpc-user**: User management and tasks via RPC.
|
|
||||||
|
|
||||||
### Tools
|
|
||||||
|
|
||||||
- **changelog**: Track and manage changes in OpenIM.
|
|
||||||
- **component**: Utilities related to different components within OpenIM.
|
|
||||||
- **infra**: Infrastructure and backend management tools.
|
|
||||||
- **ncpu**: Monitor and manage CPU usage and related tasks.
|
|
||||||
- **yamlfmt**: A tool for formatting and linting YAML files within the OpenIM configuration.
|
|
||||||
|
|
||||||
## How to Use
|
|
||||||
|
|
||||||
To view the manual page for any of the OpenIM commands, use the `man` command followed by the command name. For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
man openim-api
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributions
|
|
||||||
|
|
||||||
We welcome contributions to enhance the `man` pages. If you discover inconsistencies, errors, or areas where further details are required, feel free to raise an issue or submit a pull request.
|
|
@ -1,62 +0,0 @@
|
|||||||
// 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 main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/cmd/genutils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// TODO use os.Args instead of "flags" because "flags" will mess up the man pages!
|
|
||||||
path := "docs/man/man1"
|
|
||||||
module := ""
|
|
||||||
if len(os.Args) == 3 {
|
|
||||||
path = os.Args[1]
|
|
||||||
module = os.Args[2]
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(os.Stderr, "usage: %s [output directory] [module] \n", os.Args[0])
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
outDir, err := genutils.OutDir(path)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set environment variables used by command so the output is consistent,
|
|
||||||
// regardless of where we run.
|
|
||||||
os.Setenv("HOME", "/home/username")
|
|
||||||
|
|
||||||
// openim-api
|
|
||||||
// openim-cmdutils
|
|
||||||
// openim-crontask
|
|
||||||
// openim-msggateway
|
|
||||||
// openim-msgtransfer
|
|
||||||
// openim-push
|
|
||||||
// openim-rpc-auth
|
|
||||||
// openim-rpc-conversation
|
|
||||||
// openim-rpc-friend
|
|
||||||
// openim-rpc-group
|
|
||||||
// openim-rpc-msg
|
|
||||||
// openim-rpc-third
|
|
||||||
// openim-rpc-user
|
|
||||||
switch module {
|
|
||||||
case "openim-api":
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,243 +0,0 @@
|
|||||||
// 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 genericclioptions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/AlekSi/pointer"
|
|
||||||
"github.com/marmotedu/marmotedu-sdk-go/rest"
|
|
||||||
"github.com/marmotedu/marmotedu-sdk-go/tools/clientcmd"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Defines flag for imctl.
|
|
||||||
const (
|
|
||||||
FlagIMConfig = "imconfig"
|
|
||||||
FlagBearerToken = "user.token"
|
|
||||||
FlagUsername = "user.username"
|
|
||||||
FlagPassword = "user.password"
|
|
||||||
FlagSecretID = "user.secret-id"
|
|
||||||
FlagSecretKey = "user.secret-key"
|
|
||||||
FlagCertFile = "user.client-certificate"
|
|
||||||
FlagKeyFile = "user.client-key"
|
|
||||||
FlagTLSServerName = "server.tls-server-name"
|
|
||||||
FlagInsecure = "server.insecure-skip-tls-verify"
|
|
||||||
FlagCAFile = "server.certificate-authority"
|
|
||||||
FlagAPIServer = "server.address"
|
|
||||||
FlagTimeout = "server.timeout"
|
|
||||||
FlagMaxRetries = "server.max-retries"
|
|
||||||
FlagRetryInterval = "server.retry-interval"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RESTClientGetter is an interface that the ConfigFlags describe to provide an easier way to mock for commands
|
|
||||||
// and eliminate the direct coupling to a struct type. Users may wish to duplicate this type in their own packages
|
|
||||||
// as per the golang type overlapping.
|
|
||||||
type RESTClientGetter interface {
|
|
||||||
// ToRESTConfig returns restconfig
|
|
||||||
ToRESTConfig() (*rest.Config, error)
|
|
||||||
// ToRawIMConfigLoader return imconfig loader as-is
|
|
||||||
ToRawIMConfigLoader() clientcmd.ClientConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ RESTClientGetter = &ConfigFlags{}
|
|
||||||
|
|
||||||
// ConfigFlags composes the set of values necessary
|
|
||||||
// for obtaining a REST client config.
|
|
||||||
type ConfigFlags struct {
|
|
||||||
IMConfig *string
|
|
||||||
|
|
||||||
BearerToken *string
|
|
||||||
Username *string
|
|
||||||
Password *string
|
|
||||||
SecretID *string
|
|
||||||
SecretKey *string
|
|
||||||
|
|
||||||
Insecure *bool
|
|
||||||
TLSServerName *string
|
|
||||||
CertFile *string
|
|
||||||
KeyFile *string
|
|
||||||
CAFile *string
|
|
||||||
|
|
||||||
APIServer *string
|
|
||||||
Timeout *time.Duration
|
|
||||||
MaxRetries *int
|
|
||||||
RetryInterval *time.Duration
|
|
||||||
|
|
||||||
clientConfig clientcmd.ClientConfig
|
|
||||||
lock sync.Mutex
|
|
||||||
// If set to true, will use persistent client config and
|
|
||||||
// propagate the config to the places that need it, rather than
|
|
||||||
// loading the config multiple times
|
|
||||||
usePersistentConfig bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToRESTConfig implements RESTClientGetter.
|
|
||||||
// Returns a REST client configuration based on a provided path
|
|
||||||
// to a .imconfig file, loading rules, and config flag overrides.
|
|
||||||
// Expects the AddFlags method to have been called.
|
|
||||||
func (f *ConfigFlags) ToRESTConfig() (*rest.Config, error) {
|
|
||||||
return f.ToRawIMConfigLoader().ClientConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToRawIMConfigLoader binds config flag values to config overrides
|
|
||||||
// Returns an interactive clientConfig if the password flag is enabled,
|
|
||||||
// or a non-interactive clientConfig otherwise.
|
|
||||||
func (f *ConfigFlags) ToRawIMConfigLoader() clientcmd.ClientConfig {
|
|
||||||
if f.usePersistentConfig {
|
|
||||||
return f.toRawIMPersistentConfigLoader()
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.toRawIMConfigLoader()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *ConfigFlags) toRawIMConfigLoader() clientcmd.ClientConfig {
|
|
||||||
config := clientcmd.NewConfig()
|
|
||||||
if err := viper.Unmarshal(&config); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return clientcmd.NewClientConfigFromConfig(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toRawIMPersistentConfigLoader binds config flag values to config overrides
|
|
||||||
// Returns a persistent clientConfig for propagation.
|
|
||||||
func (f *ConfigFlags) toRawIMPersistentConfigLoader() clientcmd.ClientConfig {
|
|
||||||
f.lock.Lock()
|
|
||||||
defer f.lock.Unlock()
|
|
||||||
|
|
||||||
if f.clientConfig == nil {
|
|
||||||
f.clientConfig = f.toRawIMConfigLoader()
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.clientConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFlags binds client configuration flags to a given flagset.
|
|
||||||
func (f *ConfigFlags) AddFlags(flags *pflag.FlagSet) {
|
|
||||||
if f.IMConfig != nil {
|
|
||||||
flags.StringVar(f.IMConfig, FlagIMConfig, *f.IMConfig,
|
|
||||||
fmt.Sprintf("Path to the %s file to use for CLI requests", FlagIMConfig))
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.BearerToken != nil {
|
|
||||||
flags.StringVar(
|
|
||||||
f.BearerToken,
|
|
||||||
FlagBearerToken,
|
|
||||||
*f.BearerToken,
|
|
||||||
"Bearer token for authentication to the API server",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Username != nil {
|
|
||||||
flags.StringVar(f.Username, FlagUsername, *f.Username, "Username for basic authentication to the API server")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Password != nil {
|
|
||||||
flags.StringVar(f.Password, FlagPassword, *f.Password, "Password for basic authentication to the API server")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.SecretID != nil {
|
|
||||||
flags.StringVar(f.SecretID, FlagSecretID, *f.SecretID, "SecretID for JWT authentication to the API server")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.SecretKey != nil {
|
|
||||||
flags.StringVar(f.SecretKey, FlagSecretKey, *f.SecretKey, "SecretKey for jwt authentication to the API server")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.CertFile != nil {
|
|
||||||
flags.StringVar(f.CertFile, FlagCertFile, *f.CertFile, "Path to a client certificate file for TLS")
|
|
||||||
}
|
|
||||||
if f.KeyFile != nil {
|
|
||||||
flags.StringVar(f.KeyFile, FlagKeyFile, *f.KeyFile, "Path to a client key file for TLS")
|
|
||||||
}
|
|
||||||
if f.TLSServerName != nil {
|
|
||||||
flags.StringVar(f.TLSServerName, FlagTLSServerName, *f.TLSServerName, ""+
|
|
||||||
"Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used")
|
|
||||||
}
|
|
||||||
if f.Insecure != nil {
|
|
||||||
flags.BoolVar(f.Insecure, FlagInsecure, *f.Insecure, ""+
|
|
||||||
"If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure")
|
|
||||||
}
|
|
||||||
if f.CAFile != nil {
|
|
||||||
flags.StringVar(f.CAFile, FlagCAFile, *f.CAFile, "Path to a cert file for the certificate authority")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.APIServer != nil {
|
|
||||||
flags.StringVarP(f.APIServer, FlagAPIServer, "s", *f.APIServer, "The address and port of the IM API server")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Timeout != nil {
|
|
||||||
flags.DurationVar(
|
|
||||||
f.Timeout,
|
|
||||||
FlagTimeout,
|
|
||||||
*f.Timeout,
|
|
||||||
"The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests.",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.MaxRetries != nil {
|
|
||||||
flag.IntVar(f.MaxRetries, FlagMaxRetries, *f.MaxRetries, "Maximum number of retries.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.RetryInterval != nil {
|
|
||||||
flags.DurationVar(
|
|
||||||
f.RetryInterval,
|
|
||||||
FlagRetryInterval,
|
|
||||||
*f.RetryInterval,
|
|
||||||
"The interval time between each attempt.",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithDeprecatedPasswordFlag enables the username and password config flags.
|
|
||||||
func (f *ConfigFlags) WithDeprecatedPasswordFlag() *ConfigFlags {
|
|
||||||
f.Username = pointer.ToString("")
|
|
||||||
f.Password = pointer.ToString("")
|
|
||||||
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithDeprecatedSecretFlag enables the secretID and secretKey config flags.
|
|
||||||
func (f *ConfigFlags) WithDeprecatedSecretFlag() *ConfigFlags {
|
|
||||||
f.SecretID = pointer.ToString("")
|
|
||||||
f.SecretKey = pointer.ToString("")
|
|
||||||
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConfigFlags returns ConfigFlags with default values set.
|
|
||||||
func NewConfigFlags(usePersistentConfig bool) *ConfigFlags {
|
|
||||||
return &ConfigFlags{
|
|
||||||
IMConfig: pointer.ToString(""),
|
|
||||||
|
|
||||||
BearerToken: pointer.ToString(""),
|
|
||||||
Insecure: pointer.ToBool(false),
|
|
||||||
TLSServerName: pointer.ToString(""),
|
|
||||||
CertFile: pointer.ToString(""),
|
|
||||||
KeyFile: pointer.ToString(""),
|
|
||||||
CAFile: pointer.ToString(""),
|
|
||||||
|
|
||||||
APIServer: pointer.ToString(""),
|
|
||||||
Timeout: pointer.ToDuration(30 * time.Second),
|
|
||||||
MaxRetries: pointer.ToInt(0),
|
|
||||||
RetryInterval: pointer.ToDuration(1 * time.Second),
|
|
||||||
usePersistentConfig: usePersistentConfig,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
// iamctl is the command line tool for iam platform.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
command := cmd.NewDefaultIMCtlCommand()
|
|
||||||
if err := command.Execute(); err != nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
module github.com/OpenIMSDK/Open-IM-Server/tools/imctl
|
|
||||||
|
|
||||||
go 1.18
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/MakeNowJust/heredoc/v2 v2.0.1
|
|
||||||
github.com/mitchellh/go-wordwrap v1.0.1
|
|
||||||
github.com/moby/term v0.5.0
|
|
||||||
github.com/russross/blackfriday v1.6.0
|
|
||||||
github.com/spf13/cobra v1.7.0
|
|
||||||
github.com/spf13/pflag v1.0.5
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
||||||
golang.org/x/sys v0.1.0 // indirect
|
|
||||||
)
|
|
@ -1,24 +0,0 @@
|
|||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
|
||||||
github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZYIR/J6A=
|
|
||||||
github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
|
||||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
|
||||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
|
||||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
|
||||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
|
||||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
|
||||||
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
|
|
||||||
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
||||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
|
||||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -1,146 +0,0 @@
|
|||||||
// 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 cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
cliflag "github.com/openimsdk/component-base/pkg/cli/flag"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/pkg/cli/genericclioptions"
|
|
||||||
|
|
||||||
cmdutil "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/inernal/iamctl/cmd/util"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/color"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/completion"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/info"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/jwt"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/new"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/options"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/policy"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/secret"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/set"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/user"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/validate"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/cmd/version"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/imctl/util/templates"
|
|
||||||
genericapiserver "github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/pkg/server"
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/pkg/cli/genericclioptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewDefaultIAMCtlCommand creates the `imctl` command with default arguments.
|
|
||||||
func NewDefaultIMCtlCommand() *cobra.Command {
|
|
||||||
return NewIMCtlCommand(os.Stdin, os.Stdout, os.Stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIAMCtlCommand returns new initialized instance of 'imctl' root command.
|
|
||||||
func NewIMCtlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
|
|
||||||
// Parent command to which all subcommands are added.
|
|
||||||
cmds := &cobra.Command{
|
|
||||||
Use: "imctl",
|
|
||||||
Short: "imctl controls the IM platform",
|
|
||||||
Long: templates.LongDesc(`
|
|
||||||
imctl controls the IM platform, is the client side tool for IM platform.
|
|
||||||
|
|
||||||
Find more information at:
|
|
||||||
// TODO: add link to docs, from auto scripts and gendocs
|
|
||||||
https://github.com/OpenIMSDK/Open-IM-Server/tree/main/docs`),
|
|
||||||
Run: runHelp,
|
|
||||||
// Hook before and after Run initialize and write profiles to disk,
|
|
||||||
// respectively.
|
|
||||||
PersistentPreRunE: func(*cobra.Command, []string) error {
|
|
||||||
return initProfiling()
|
|
||||||
},
|
|
||||||
PersistentPostRunE: func(*cobra.Command, []string) error {
|
|
||||||
return flushProfiling()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
flags := cmds.PersistentFlags()
|
|
||||||
flags.SetNormalizeFunc(cliflag.WarnWordSepNormalizeFunc) // Warn for "_" flags
|
|
||||||
|
|
||||||
// Normalize all flags that are coming from other packages or pre-configurations
|
|
||||||
// a.k.a. change all "_" to "-". e.g. glog package
|
|
||||||
flags.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
|
|
||||||
|
|
||||||
addProfilingFlags(flags)
|
|
||||||
|
|
||||||
iamConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag().WithDeprecatedSecretFlag()
|
|
||||||
iamConfigFlags.AddFlags(flags)
|
|
||||||
matchVersionIAMConfigFlags := cmdutil.NewMatchVersionFlags(iamConfigFlags)
|
|
||||||
matchVersionIAMConfigFlags.AddFlags(cmds.PersistentFlags())
|
|
||||||
|
|
||||||
_ = viper.BindPFlags(cmds.PersistentFlags())
|
|
||||||
cobra.OnInitialize(func() {
|
|
||||||
genericapiserver.LoadConfig(viper.GetString(genericclioptions.FlagIAMConfig), "iamctl")
|
|
||||||
})
|
|
||||||
cmds.PersistentFlags().AddGoFlagSet(flag.CommandLine)
|
|
||||||
|
|
||||||
f := cmdutil.NewFactory(matchVersionIAMConfigFlags)
|
|
||||||
|
|
||||||
// From this point and forward we get warnings on flags that contain "_" separators
|
|
||||||
cmds.SetGlobalNormalizationFunc(cliflag.WarnWordSepNormalizeFunc)
|
|
||||||
|
|
||||||
ioStreams := genericclioptions.IOStreams{In: in, Out: out, ErrOut: err}
|
|
||||||
|
|
||||||
groups := templates.CommandGroups{
|
|
||||||
{
|
|
||||||
Message: "Basic Commands:",
|
|
||||||
Commands: []*cobra.Command{
|
|
||||||
info.NewCmdInfo(f, ioStreams),
|
|
||||||
color.NewCmdColor(f, ioStreams),
|
|
||||||
new.NewCmdNew(f, ioStreams),
|
|
||||||
jwt.NewCmdJWT(f, ioStreams),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: "Identity and Access Management Commands:",
|
|
||||||
Commands: []*cobra.Command{
|
|
||||||
user.NewCmdUser(f, ioStreams),
|
|
||||||
secret.NewCmdSecret(f, ioStreams),
|
|
||||||
policy.NewCmdPolicy(f, ioStreams),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: "Troubleshooting and Debugging Commands:",
|
|
||||||
Commands: []*cobra.Command{
|
|
||||||
validate.NewCmdValidate(f, ioStreams),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Message: "Settings Commands:",
|
|
||||||
Commands: []*cobra.Command{
|
|
||||||
set.NewCmdSet(f, ioStreams),
|
|
||||||
completion.NewCmdCompletion(ioStreams.Out, ""),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
groups.Add(cmds)
|
|
||||||
|
|
||||||
filters := []string{"options"}
|
|
||||||
templates.ActsAsRootCommand(cmds, filters, groups...)
|
|
||||||
|
|
||||||
cmds.AddCommand(version.NewCmdVersion(f, ioStreams))
|
|
||||||
cmds.AddCommand(options.NewCmdOptions(ioStreams.Out))
|
|
||||||
|
|
||||||
return cmds
|
|
||||||
}
|
|
||||||
|
|
||||||
func runHelp(cmd *cobra.Command, args []string) {
|
|
||||||
_ = cmd.Help()
|
|
||||||
}
|
|
@ -1,352 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
imctl color
|
|
||||||
|
|
||||||
# Print supported colors by type
|
|
||||||
imctl color -t fg-hi
|
|
||||||
|
|
||||||
# Print all supported colors
|
|
||||||
imctl 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
|
|
||||||
}
|
|
@ -1,283 +0,0 @@
|
|||||||
// 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
|
|
||||||
}
|
|
@ -1,119 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
// 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"
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,186 +0,0 @@
|
|||||||
// 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"
|
|
||||||
|
|
||||||
"github.com/marmotedu/iam/internal/pkg/middleware/auth"
|
|
||||||
|
|
||||||
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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,150 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,619 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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/OpenIMSDK/Open-IM-Server/tools/imctl/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/OpenIMSDK/Open-IM-Server/tools/imctl/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/OpenIMSDK/Open-IM-Server/tools/imctl/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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
// 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
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,123 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
// 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 cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"runtime/pprof"
|
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// profiling configuration variables
|
|
||||||
var (
|
|
||||||
profileName string = "none" // Name of the profile to capture.
|
|
||||||
profileOutput string = "profile.pprof" // File to write the profile data.
|
|
||||||
)
|
|
||||||
|
|
||||||
// addProfilingFlags registers profiling related flags to the given FlagSet.
|
|
||||||
func addProfilingFlags(flags *pflag.FlagSet) {
|
|
||||||
flags.StringVar(
|
|
||||||
&profileName,
|
|
||||||
"profile",
|
|
||||||
"none",
|
|
||||||
"Type of profile to capture. Options: none, cpu, heap, goroutine, threadcreate, block, mutex",
|
|
||||||
)
|
|
||||||
flags.StringVar(&profileOutput, "profile-output", "profile.pprof", "File to write the profile data")
|
|
||||||
}
|
|
||||||
|
|
||||||
// initProfiling sets up profiling based on the user's choice.
|
|
||||||
// If 'cpu' is selected, it starts the CPU profile. For block and mutex profiles,
|
|
||||||
// sampling rates are set up.
|
|
||||||
func initProfiling() error {
|
|
||||||
switch profileName {
|
|
||||||
case "none":
|
|
||||||
return nil
|
|
||||||
case "cpu":
|
|
||||||
f, err := os.Create(profileOutput)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return pprof.StartCPUProfile(f)
|
|
||||||
case "block":
|
|
||||||
runtime.SetBlockProfileRate(1) // Sampling every block event
|
|
||||||
return nil
|
|
||||||
case "mutex":
|
|
||||||
runtime.SetMutexProfileFraction(1) // Sampling every mutex event
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
if profile := pprof.Lookup(profileName); profile == nil {
|
|
||||||
return fmt.Errorf("unknown profile type: '%s'", profileName)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// flushProfiling writes the profiling data to the specified file.
|
|
||||||
// For heap profiles, it runs the GC before capturing the data.
|
|
||||||
// It stops the CPU profile if it was started.
|
|
||||||
func flushProfiling() error {
|
|
||||||
switch profileName {
|
|
||||||
case "none":
|
|
||||||
return nil
|
|
||||||
case "cpu":
|
|
||||||
pprof.StopCPUProfile()
|
|
||||||
return nil
|
|
||||||
case "heap":
|
|
||||||
runtime.GC() // Run garbage collection before writing heap profile
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
profile := pprof.Lookup(profileName)
|
|
||||||
if profile == nil {
|
|
||||||
return errors.New("invalid profile type")
|
|
||||||
}
|
|
||||||
f, err := os.Create(profileOutput)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return profile.WriteTo(f, 0)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,145 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,213 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,129 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,142 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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)
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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)
|
|
||||||
}
|
|
@ -1,397 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,141 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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)
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,166 +0,0 @@
|
|||||||
// 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/OpenIMSDK/Open-IM-Server/tools/imctl/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
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
// 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 interrupt deal with signals.
|
|
||||||
package interrupt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// terminationSignals are signals that cause the program to exit in the
|
|
||||||
// supported platforms (linux, darwin, windows).
|
|
||||||
var terminationSignals = []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT}
|
|
||||||
|
|
||||||
// Handler guarantees execution of notifications after a critical section (the function passed
|
|
||||||
// to a Run method), even in the presence of process termination. It guarantees exactly once
|
|
||||||
// invocation of the provided notify functions.
|
|
||||||
type Handler struct {
|
|
||||||
notify []func()
|
|
||||||
final func(os.Signal)
|
|
||||||
once sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chain creates a new handler that invokes all notify functions when the critical section exits
|
|
||||||
// and then invokes the optional handler's notifications. This allows critical sections to be
|
|
||||||
// nested without losing exactly once invocations. Notify functions can invoke any cleanup needed
|
|
||||||
// but should not exit (which is the responsibility of the parent handler).
|
|
||||||
func Chain(handler *Handler, notify ...func()) *Handler {
|
|
||||||
if handler == nil {
|
|
||||||
return New(nil, notify...)
|
|
||||||
}
|
|
||||||
return New(handler.Signal, append(notify, handler.Close)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new handler that guarantees all notify functions are run after the critical
|
|
||||||
// section exits (or is interrupted by the OS), then invokes the final handler. If no final
|
|
||||||
// handler is specified, the default final is `os.Exit(1)`. A handler can only be used for
|
|
||||||
// one critical section.
|
|
||||||
func New(final func(os.Signal), notify ...func()) *Handler {
|
|
||||||
return &Handler{
|
|
||||||
final: final,
|
|
||||||
notify: notify,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close executes all the notification handlers if they have not yet been executed.
|
|
||||||
func (h *Handler) Close() {
|
|
||||||
h.once.Do(func() {
|
|
||||||
for _, fn := range h.notify {
|
|
||||||
fn()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signal is called when an os.Signal is received, and guarantees that all notifications
|
|
||||||
// are executed, then the final handler is executed. This function should only be called once
|
|
||||||
// per Handler instance.
|
|
||||||
func (h *Handler) Signal(s os.Signal) {
|
|
||||||
h.once.Do(func() {
|
|
||||||
for _, fn := range h.notify {
|
|
||||||
fn()
|
|
||||||
}
|
|
||||||
if h.final == nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
h.final(s)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run ensures that any notifications are invoked after the provided fn exits (even if the
|
|
||||||
// process is interrupted by an OS termination signal). Notifications are only invoked once
|
|
||||||
// per Handler instance, so calling Run more than once will not behave as the user expects.
|
|
||||||
func (h *Handler) Run(fn func() error) error {
|
|
||||||
ch := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(ch, terminationSignals...)
|
|
||||||
defer func() {
|
|
||||||
signal.Stop(ch)
|
|
||||||
close(ch)
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
sig, ok := <-ch
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
h.Signal(sig)
|
|
||||||
}()
|
|
||||||
defer h.Close()
|
|
||||||
return fn()
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
// 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 templates
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CommandGroup struct {
|
|
||||||
Message string
|
|
||||||
Commands []*cobra.Command
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommandGroups []CommandGroup
|
|
||||||
|
|
||||||
func (g CommandGroups) Add(c *cobra.Command) {
|
|
||||||
for _, group := range g {
|
|
||||||
c.AddCommand(group.Commands...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g CommandGroups) Has(c *cobra.Command) bool {
|
|
||||||
for _, group := range g {
|
|
||||||
for _, command := range group.Commands {
|
|
||||||
if command == c {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddAdditionalCommands(g CommandGroups, message string, cmds []*cobra.Command) CommandGroups {
|
|
||||||
group := CommandGroup{Message: message}
|
|
||||||
for _, c := range cmds {
|
|
||||||
// Don't show commands that have no short description
|
|
||||||
if !g.Has(c) && len(c.Short) != 0 {
|
|
||||||
group.Commands = append(group.Commands, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(group.Commands) == 0 {
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
return append(g, group)
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
// 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 templates
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/russross/blackfriday"
|
|
||||||
)
|
|
||||||
|
|
||||||
const linebreak = "\n"
|
|
||||||
|
|
||||||
// ASCIIRenderer implements blackfriday.Renderer.
|
|
||||||
var _ blackfriday.Renderer = &ASCIIRenderer{}
|
|
||||||
|
|
||||||
// ASCIIRenderer is a blackfriday.Renderer intended for rendering markdown
|
|
||||||
// documents as plain text, well suited for human reading on terminals.
|
|
||||||
type ASCIIRenderer struct {
|
|
||||||
Indentation string
|
|
||||||
|
|
||||||
listItemCount uint
|
|
||||||
listLevel uint
|
|
||||||
}
|
|
||||||
|
|
||||||
// NormalText gets a text chunk *after* the markdown syntax was already
|
|
||||||
// processed and does a final cleanup on things we don't expect here, like
|
|
||||||
// removing linebreaks on things that are not a paragraph break (auto unwrap).
|
|
||||||
func (r *ASCIIRenderer) NormalText(out *bytes.Buffer, text []byte) {
|
|
||||||
raw := string(text)
|
|
||||||
lines := strings.Split(raw, linebreak)
|
|
||||||
for _, line := range lines {
|
|
||||||
trimmed := strings.Trim(line, " \n\t")
|
|
||||||
if len(trimmed) > 0 && trimmed[0] != '_' {
|
|
||||||
out.WriteString(" ")
|
|
||||||
}
|
|
||||||
out.WriteString(trimmed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// List renders the start and end of a list.
|
|
||||||
func (r *ASCIIRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
|
|
||||||
r.listLevel++
|
|
||||||
out.WriteString(linebreak)
|
|
||||||
text()
|
|
||||||
r.listLevel--
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListItem renders list items and supports both ordered and unordered lists.
|
|
||||||
func (r *ASCIIRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
|
|
||||||
if flags&blackfriday.LIST_ITEM_BEGINNING_OF_LIST != 0 {
|
|
||||||
r.listItemCount = 1
|
|
||||||
} else {
|
|
||||||
r.listItemCount++
|
|
||||||
}
|
|
||||||
indent := strings.Repeat(r.Indentation, int(r.listLevel))
|
|
||||||
var bullet string
|
|
||||||
if flags&blackfriday.LIST_TYPE_ORDERED != 0 {
|
|
||||||
bullet += fmt.Sprintf("%d.", r.listItemCount)
|
|
||||||
} else {
|
|
||||||
bullet += "*"
|
|
||||||
}
|
|
||||||
out.WriteString(indent + bullet + " ")
|
|
||||||
r.fw(out, text)
|
|
||||||
out.WriteString(linebreak)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Paragraph renders the start and end of a paragraph.
|
|
||||||
func (r *ASCIIRenderer) Paragraph(out *bytes.Buffer, text func() bool) {
|
|
||||||
out.WriteString(linebreak)
|
|
||||||
text()
|
|
||||||
out.WriteString(linebreak)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockCode renders a chunk of text that represents source code.
|
|
||||||
func (r *ASCIIRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
|
|
||||||
out.WriteString(linebreak)
|
|
||||||
lines := []string{}
|
|
||||||
for _, line := range strings.Split(string(text), linebreak) {
|
|
||||||
indented := r.Indentation + line
|
|
||||||
lines = append(lines, indented)
|
|
||||||
}
|
|
||||||
out.WriteString(strings.Join(lines, linebreak))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ASCIIRenderer) GetFlags() int { return 0 }
|
|
||||||
func (r *ASCIIRenderer) HRule(out *bytes.Buffer) {
|
|
||||||
out.WriteString(linebreak + "----------" + linebreak)
|
|
||||||
}
|
|
||||||
func (r *ASCIIRenderer) LineBreak(out *bytes.Buffer) { out.WriteString(linebreak) }
|
|
||||||
func (r *ASCIIRenderer) TitleBlock(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) Header(out *bytes.Buffer, text func() bool, level int, id string) { text() }
|
|
||||||
func (r *ASCIIRenderer) BlockHtml(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) BlockQuote(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) TableRow(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) TableHeaderCell(out *bytes.Buffer, text []byte, align int) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) TableCell(out *bytes.Buffer, text []byte, align int) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) Footnotes(out *bytes.Buffer, text func() bool) { text() }
|
|
||||||
func (r *ASCIIRenderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {
|
|
||||||
r.fw(out, text)
|
|
||||||
}
|
|
||||||
func (r *ASCIIRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) { r.fw(out, link) }
|
|
||||||
func (r *ASCIIRenderer) CodeSpan(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) DoubleEmphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) Emphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) RawHtmlTag(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) TripleEmphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) StrikeThrough(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int) { r.fw(out, ref) }
|
|
||||||
func (r *ASCIIRenderer) Entity(out *bytes.Buffer, entity []byte) { r.fw(out, entity) }
|
|
||||||
func (r *ASCIIRenderer) Smartypants(out *bytes.Buffer, text []byte) { r.fw(out, text) }
|
|
||||||
func (r *ASCIIRenderer) DocumentHeader(out *bytes.Buffer) {}
|
|
||||||
func (r *ASCIIRenderer) DocumentFooter(out *bytes.Buffer) {}
|
|
||||||
func (r *ASCIIRenderer) TocHeaderWithAnchor(text []byte, level int, anchor string) {}
|
|
||||||
func (r *ASCIIRenderer) TocHeader(text []byte, level int) {}
|
|
||||||
func (r *ASCIIRenderer) TocFinalize() {}
|
|
||||||
|
|
||||||
func (r *ASCIIRenderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {
|
|
||||||
r.fw(out, header, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ASCIIRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
|
|
||||||
out.WriteString(" ")
|
|
||||||
r.fw(out, link)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ASCIIRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
|
|
||||||
r.fw(out, link)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ASCIIRenderer) fw(out io.Writer, text ...[]byte) {
|
|
||||||
for _, t := range text {
|
|
||||||
out.Write(t)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
// 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 templates
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/MakeNowJust/heredoc/v2"
|
|
||||||
"github.com/russross/blackfriday"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
const Indentation = ` `
|
|
||||||
|
|
||||||
// LongDesc normalizes a command's long description to follow the conventions.
|
|
||||||
func LongDesc(s string) string {
|
|
||||||
if len(s) == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return normalizer{s}.heredoc().markdown().trim().string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Examples normalizes a command's examples to follow the conventions.
|
|
||||||
func Examples(s string) string {
|
|
||||||
if len(s) == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return normalizer{s}.trim().indent().string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize perform all required normalizations on a given command.
|
|
||||||
func Normalize(cmd *cobra.Command) *cobra.Command {
|
|
||||||
if len(cmd.Long) > 0 {
|
|
||||||
cmd.Long = LongDesc(cmd.Long)
|
|
||||||
}
|
|
||||||
if len(cmd.Example) > 0 {
|
|
||||||
cmd.Example = Examples(cmd.Example)
|
|
||||||
}
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// NormalizeAll perform all required normalizations in the entire command tree.
|
|
||||||
func NormalizeAll(cmd *cobra.Command) *cobra.Command {
|
|
||||||
if cmd.HasSubCommands() {
|
|
||||||
for _, subCmd := range cmd.Commands() {
|
|
||||||
NormalizeAll(subCmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Normalize(cmd)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
type normalizer struct {
|
|
||||||
string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s normalizer) markdown() normalizer {
|
|
||||||
bytes := []byte(s.string)
|
|
||||||
formatted := blackfriday.Markdown(
|
|
||||||
bytes,
|
|
||||||
&ASCIIRenderer{Indentation: Indentation},
|
|
||||||
blackfriday.EXTENSION_NO_INTRA_EMPHASIS,
|
|
||||||
)
|
|
||||||
s.string = string(formatted)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s normalizer) heredoc() normalizer {
|
|
||||||
s.string = heredoc.Doc(s.string)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s normalizer) trim() normalizer {
|
|
||||||
s.string = strings.TrimSpace(s.string)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s normalizer) indent() normalizer {
|
|
||||||
indentedLines := []string{}
|
|
||||||
for _, line := range strings.Split(s.string, "\n") {
|
|
||||||
trimmed := strings.TrimSpace(line)
|
|
||||||
indented := Indentation + trimmed
|
|
||||||
indentedLines = append(indentedLines, indented)
|
|
||||||
}
|
|
||||||
s.string = strings.Join(indentedLines, "\n")
|
|
||||||
return s
|
|
||||||
}
|
|
@ -1,296 +0,0 @@
|
|||||||
// 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 templates
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
flag "github.com/spf13/pflag"
|
|
||||||
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/tools/imctl/internal/util/term"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FlagExposer interface {
|
|
||||||
ExposeFlags(cmd *cobra.Command, flags ...string) FlagExposer
|
|
||||||
}
|
|
||||||
|
|
||||||
func ActsAsRootCommand(cmd *cobra.Command, filters []string, groups ...CommandGroup) FlagExposer {
|
|
||||||
if cmd == nil {
|
|
||||||
panic("nil root command")
|
|
||||||
}
|
|
||||||
templater := &templater{
|
|
||||||
RootCmd: cmd,
|
|
||||||
UsageTemplate: MainUsageTemplate(),
|
|
||||||
HelpTemplate: MainHelpTemplate(),
|
|
||||||
CommandGroups: groups,
|
|
||||||
Filtered: filters,
|
|
||||||
}
|
|
||||||
cmd.SetFlagErrorFunc(templater.FlagErrorFunc())
|
|
||||||
cmd.SilenceUsage = true
|
|
||||||
cmd.SetUsageFunc(templater.UsageFunc())
|
|
||||||
cmd.SetHelpFunc(templater.HelpFunc())
|
|
||||||
return templater
|
|
||||||
}
|
|
||||||
|
|
||||||
func UseOptionsTemplates(cmd *cobra.Command) {
|
|
||||||
templater := &templater{
|
|
||||||
UsageTemplate: OptionsUsageTemplate(),
|
|
||||||
HelpTemplate: OptionsHelpTemplate(),
|
|
||||||
}
|
|
||||||
cmd.SetUsageFunc(templater.UsageFunc())
|
|
||||||
cmd.SetHelpFunc(templater.HelpFunc())
|
|
||||||
}
|
|
||||||
|
|
||||||
type templater struct {
|
|
||||||
UsageTemplate string
|
|
||||||
HelpTemplate string
|
|
||||||
RootCmd *cobra.Command
|
|
||||||
CommandGroups
|
|
||||||
Filtered []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) FlagErrorFunc(exposedFlags ...string) func(*cobra.Command, error) error {
|
|
||||||
return func(c *cobra.Command, err error) error {
|
|
||||||
c.SilenceUsage = true
|
|
||||||
switch c.CalledAs() {
|
|
||||||
case "options":
|
|
||||||
return fmt.Errorf("%s\nrun '%s' without flags", err, c.CommandPath())
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("%s\nsee '%s --help' for usage", err, c.CommandPath())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) ExposeFlags(cmd *cobra.Command, flags ...string) FlagExposer {
|
|
||||||
cmd.SetUsageFunc(t.UsageFunc(flags...))
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) HelpFunc() func(*cobra.Command, []string) {
|
|
||||||
return func(c *cobra.Command, s []string) {
|
|
||||||
tt := template.New("help")
|
|
||||||
tt.Funcs(t.templateFuncs())
|
|
||||||
template.Must(tt.Parse(t.HelpTemplate))
|
|
||||||
out := term.NewResponsiveWriter(c.OutOrStdout())
|
|
||||||
err := tt.Execute(out, c)
|
|
||||||
if err != nil {
|
|
||||||
c.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) UsageFunc(exposedFlags ...string) func(*cobra.Command) error {
|
|
||||||
return func(c *cobra.Command) error {
|
|
||||||
tt := template.New("usage")
|
|
||||||
tt.Funcs(t.templateFuncs(exposedFlags...))
|
|
||||||
template.Must(tt.Parse(t.UsageTemplate))
|
|
||||||
out := term.NewResponsiveWriter(c.OutOrStderr())
|
|
||||||
return tt.Execute(out, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) templateFuncs(exposedFlags ...string) template.FuncMap {
|
|
||||||
return template.FuncMap{
|
|
||||||
"trim": strings.TrimSpace,
|
|
||||||
"trimRight": func(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) },
|
|
||||||
"trimLeft": func(s string) string { return strings.TrimLeftFunc(s, unicode.IsSpace) },
|
|
||||||
"gt": cobra.Gt,
|
|
||||||
"eq": cobra.Eq,
|
|
||||||
"rpad": rpad,
|
|
||||||
"appendIfNotPresent": appendIfNotPresent,
|
|
||||||
"flagsNotIntersected": flagsNotIntersected,
|
|
||||||
"visibleFlags": visibleFlags,
|
|
||||||
"flagsUsages": flagsUsages,
|
|
||||||
"cmdGroups": t.cmdGroups,
|
|
||||||
"cmdGroupsString": t.cmdGroupsString,
|
|
||||||
"rootCmd": t.rootCmdName,
|
|
||||||
"isRootCmd": t.isRootCmd,
|
|
||||||
"optionsCmdFor": t.optionsCmdFor,
|
|
||||||
"usageLine": t.usageLine,
|
|
||||||
"exposed": func(c *cobra.Command) *flag.FlagSet {
|
|
||||||
exposed := flag.NewFlagSet("exposed", flag.ContinueOnError)
|
|
||||||
if len(exposedFlags) > 0 {
|
|
||||||
for _, name := range exposedFlags {
|
|
||||||
if flag := c.Flags().Lookup(name); flag != nil {
|
|
||||||
exposed.AddFlag(flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exposed
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) cmdGroups(c *cobra.Command, all []*cobra.Command) []CommandGroup {
|
|
||||||
if len(t.CommandGroups) > 0 && c == t.RootCmd {
|
|
||||||
all = filter(all, t.Filtered...)
|
|
||||||
return AddAdditionalCommands(t.CommandGroups, "Other Commands:", all)
|
|
||||||
}
|
|
||||||
all = filter(all, "options")
|
|
||||||
return []CommandGroup{
|
|
||||||
{
|
|
||||||
Message: "Available Commands:",
|
|
||||||
Commands: all,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) cmdGroupsString(c *cobra.Command) string {
|
|
||||||
groups := []string{}
|
|
||||||
for _, cmdGroup := range t.cmdGroups(c, c.Commands()) {
|
|
||||||
cmds := []string{cmdGroup.Message}
|
|
||||||
for _, cmd := range cmdGroup.Commands {
|
|
||||||
if cmd.IsAvailableCommand() {
|
|
||||||
cmds = append(cmds, " "+rpad(cmd.Name(), cmd.NamePadding())+" "+cmd.Short)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
groups = append(groups, strings.Join(cmds, "\n"))
|
|
||||||
}
|
|
||||||
return strings.Join(groups, "\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) rootCmdName(c *cobra.Command) string {
|
|
||||||
return t.rootCmd(c).CommandPath()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) isRootCmd(c *cobra.Command) bool {
|
|
||||||
return t.rootCmd(c) == c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) parents(c *cobra.Command) []*cobra.Command {
|
|
||||||
parents := []*cobra.Command{c}
|
|
||||||
for current := c; !t.isRootCmd(current) && current.HasParent(); {
|
|
||||||
current = current.Parent()
|
|
||||||
parents = append(parents, current)
|
|
||||||
}
|
|
||||||
return parents
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) rootCmd(c *cobra.Command) *cobra.Command {
|
|
||||||
if c != nil && !c.HasParent() {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if t.RootCmd == nil {
|
|
||||||
panic("nil root cmd")
|
|
||||||
}
|
|
||||||
return t.RootCmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) optionsCmdFor(c *cobra.Command) string {
|
|
||||||
if !c.Runnable() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
rootCmdStructure := t.parents(c)
|
|
||||||
for i := len(rootCmdStructure) - 1; i >= 0; i-- {
|
|
||||||
cmd := rootCmdStructure[i]
|
|
||||||
if _, _, err := cmd.Find([]string{"options"}); err == nil {
|
|
||||||
return cmd.CommandPath() + " options"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *templater) usageLine(c *cobra.Command) string {
|
|
||||||
usage := c.UseLine()
|
|
||||||
suffix := "[options]"
|
|
||||||
if c.HasFlags() && !strings.Contains(usage, suffix) {
|
|
||||||
usage += " " + suffix
|
|
||||||
}
|
|
||||||
return usage
|
|
||||||
}
|
|
||||||
|
|
||||||
func flagsUsages(f *flag.FlagSet) string {
|
|
||||||
x := new(bytes.Buffer)
|
|
||||||
|
|
||||||
f.VisitAll(func(flag *flag.Flag) {
|
|
||||||
if flag.Hidden {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
format := "--%s=%s: %s\n"
|
|
||||||
|
|
||||||
if flag.Value.Type() == "string" {
|
|
||||||
format = "--%s='%s': %s\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(flag.Shorthand) > 0 {
|
|
||||||
format = " -%s, " + format
|
|
||||||
} else {
|
|
||||||
format = " %s " + format
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(x, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return x.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func rpad(s string, padding int) string {
|
|
||||||
template := fmt.Sprintf("%%-%ds", padding)
|
|
||||||
return fmt.Sprintf(template, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendIfNotPresent(s, stringToAppend string) string {
|
|
||||||
if strings.Contains(s, stringToAppend) {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return s + " " + stringToAppend
|
|
||||||
}
|
|
||||||
|
|
||||||
func flagsNotIntersected(l *flag.FlagSet, r *flag.FlagSet) *flag.FlagSet {
|
|
||||||
f := flag.NewFlagSet("notIntersected", flag.ContinueOnError)
|
|
||||||
l.VisitAll(func(flag *flag.Flag) {
|
|
||||||
if r.Lookup(flag.Name) == nil {
|
|
||||||
f.AddFlag(flag)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func visibleFlags(l *flag.FlagSet) *flag.FlagSet {
|
|
||||||
hidden := "help"
|
|
||||||
f := flag.NewFlagSet("visible", flag.ContinueOnError)
|
|
||||||
l.VisitAll(func(flag *flag.Flag) {
|
|
||||||
if flag.Name != hidden {
|
|
||||||
f.AddFlag(flag)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func filter(cmds []*cobra.Command, names ...string) []*cobra.Command {
|
|
||||||
out := []*cobra.Command{}
|
|
||||||
for _, c := range cmds {
|
|
||||||
if c.Hidden {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
skip := false
|
|
||||||
for _, name := range names {
|
|
||||||
if name == c.Name() {
|
|
||||||
skip = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if skip {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out = append(out, c)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
// 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 templates provides template functions for working with templates.
|
|
||||||
package templates
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SectionVars is the help template section that declares variables to be used in the template.
|
|
||||||
SectionVars = `{{$isRootCmd := isRootCmd .}}` +
|
|
||||||
`{{$rootCmd := rootCmd .}}` +
|
|
||||||
`{{$visibleFlags := visibleFlags (flagsNotIntersected .LocalFlags .PersistentFlags)}}` +
|
|
||||||
`{{$explicitlyExposedFlags := exposed .}}` +
|
|
||||||
`{{$optionsCmdFor := optionsCmdFor .}}` +
|
|
||||||
`{{$usageLine := usageLine .}}`
|
|
||||||
|
|
||||||
// SectionAliases is the help template section that displays command aliases.
|
|
||||||
SectionAliases = `{{if gt .Aliases 0}}Aliases:
|
|
||||||
{{.NameAndAliases}}
|
|
||||||
|
|
||||||
{{end}}`
|
|
||||||
|
|
||||||
// SectionExamples is the help template section that displays command examples.
|
|
||||||
SectionExamples = `{{if .HasExample}}Examples:
|
|
||||||
{{trimRight .Example}}
|
|
||||||
|
|
||||||
{{end}}`
|
|
||||||
|
|
||||||
// SectionSubcommands is the help template section that displays the command's subcommands.
|
|
||||||
SectionSubcommands = `{{if .HasAvailableSubCommands}}{{cmdGroupsString .}}
|
|
||||||
|
|
||||||
{{end}}`
|
|
||||||
|
|
||||||
// SectionFlags is the help template section that displays the command's flags.
|
|
||||||
SectionFlags = `{{ if or $visibleFlags.HasFlags $explicitlyExposedFlags.HasFlags}}Options:
|
|
||||||
{{ if $visibleFlags.HasFlags}}{{trimRight (flagsUsages $visibleFlags)}}{{end}}{{ if $explicitlyExposedFlags.HasFlags}}{{ if $visibleFlags.HasFlags}}
|
|
||||||
{{end}}{{trimRight (flagsUsages $explicitlyExposedFlags)}}{{end}}
|
|
||||||
|
|
||||||
{{end}}`
|
|
||||||
|
|
||||||
// SectionUsage is the help template section that displays the command's usage.
|
|
||||||
SectionUsage = `{{if and .Runnable (ne .UseLine "") (ne .UseLine $rootCmd)}}Usage:
|
|
||||||
{{$usageLine}}
|
|
||||||
|
|
||||||
{{end}}`
|
|
||||||
|
|
||||||
// SectionTipsHelp is the help template section that displays the '--help' hint.
|
|
||||||
SectionTipsHelp = `{{if .HasSubCommands}}Use "{{$rootCmd}} <command> --help" for more information about a given command.
|
|
||||||
{{end}}`
|
|
||||||
|
|
||||||
// SectionTipsGlobalOptions is the help template section that displays the 'options' hint for displaying global
|
|
||||||
// flags.
|
|
||||||
SectionTipsGlobalOptions = `{{if $optionsCmdFor}}Use "{{$optionsCmdFor}}" for a list of global command-line options (applies to all commands).
|
|
||||||
{{end}}`
|
|
||||||
)
|
|
||||||
|
|
||||||
// MainHelpTemplate if the template for 'help' used by most commands.
|
|
||||||
func MainHelpTemplate() string {
|
|
||||||
return `{{with or .Long .Short }}{{. | trim}}{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MainUsageTemplate if the template for 'usage' used by most commands.
|
|
||||||
func MainUsageTemplate() string {
|
|
||||||
sections := []string{
|
|
||||||
"\n\n",
|
|
||||||
SectionVars,
|
|
||||||
SectionAliases,
|
|
||||||
SectionExamples,
|
|
||||||
SectionSubcommands,
|
|
||||||
SectionFlags,
|
|
||||||
SectionUsage,
|
|
||||||
SectionTipsHelp,
|
|
||||||
SectionTipsGlobalOptions,
|
|
||||||
}
|
|
||||||
return strings.TrimRightFunc(strings.Join(sections, ""), unicode.IsSpace)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OptionsHelpTemplate if the template for 'help' used by the 'options' command.
|
|
||||||
func OptionsHelpTemplate() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// OptionsUsageTemplate if the template for 'usage' used by the 'options' command.
|
|
||||||
func OptionsUsageTemplate() string {
|
|
||||||
return `{{ if .HasInheritedFlags}}The following options can be passed to any command:
|
|
||||||
|
|
||||||
{{flagsUsages .InheritedFlags}}{{end}}`
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
// 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 term
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/moby/term"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TerminalSize represents the width and height of a terminal.
|
|
||||||
type TerminalSize struct {
|
|
||||||
Width uint16
|
|
||||||
Height uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerminalSizeQueue is capable of returning terminal resize events as they occur.
|
|
||||||
type TerminalSizeQueue interface {
|
|
||||||
// Next returns the new terminal size after the terminal has been resized. It returns nil when
|
|
||||||
// monitoring has been stopped.
|
|
||||||
Next() *TerminalSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSize returns the current size of the user's terminal. If it isn't a terminal,
|
|
||||||
// nil is returned.
|
|
||||||
func (t TTY) GetSize() *TerminalSize {
|
|
||||||
outFd, isTerminal := term.GetFdInfo(t.Out)
|
|
||||||
if !isTerminal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return GetSize(outFd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSize returns the current size of the terminal associated with fd.
|
|
||||||
func GetSize(fd uintptr) *TerminalSize {
|
|
||||||
winsize, err := term.GetWinsize(fd)
|
|
||||||
if err != nil {
|
|
||||||
// runtime.HandleError(fmt.Errorf("unable to get terminal size: %v", err))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &TerminalSize{Width: winsize.Width, Height: winsize.Height}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
// 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 term provides structures and helper functions to work with
|
|
||||||
// terminal (state, sizes).
|
|
||||||
package term
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TTY helps invoke a function and preserve the state of the terminal, even if the process is
|
|
||||||
// terminated during execution. It also provides support for terminal resizing for remote command
|
|
||||||
// execution/attachment.
|
|
||||||
type TTY struct {
|
|
||||||
// In is a reader representing stdin. It is a required field.
|
|
||||||
In io.Reader
|
|
||||||
// Out is a writer representing stdout. It must be set to support terminal resizing. It is an
|
|
||||||
// optional field.
|
|
||||||
Out io.Writer
|
|
||||||
// Raw is true if the terminal should be set raw.
|
|
||||||
Raw bool
|
|
||||||
// TryDev indicates the TTY should try to open /dev/tty if the provided input
|
|
||||||
// is not a file descriptor.
|
|
||||||
TryDev bool
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
// 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 term
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
wordwrap "github.com/mitchellh/go-wordwrap"
|
|
||||||
"github.com/moby/term"
|
|
||||||
)
|
|
||||||
|
|
||||||
type wordWrapWriter struct {
|
|
||||||
limit uint
|
|
||||||
writer io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewResponsiveWriter creates a Writer that detects the column width of the
|
|
||||||
// terminal we are in, and adjusts every line width to fit and use recommended
|
|
||||||
// terminal sizes for better readability. Does proper word wrapping automatically.
|
|
||||||
//
|
|
||||||
// if terminal width >= 120 columns use 120 columns
|
|
||||||
// if terminal width >= 100 columns use 100 columns
|
|
||||||
// if terminal width >= 80 columns use 80 columns
|
|
||||||
//
|
|
||||||
// In case we're not in a terminal or if it's smaller than 80 columns width,
|
|
||||||
// doesn't do any wrapping.
|
|
||||||
func NewResponsiveWriter(w io.Writer) io.Writer {
|
|
||||||
file, ok := w.(*os.File)
|
|
||||||
if !ok {
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
fd := file.Fd()
|
|
||||||
if !term.IsTerminal(fd) {
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
terminalSize := GetSize(fd)
|
|
||||||
if terminalSize == nil {
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
var limit uint
|
|
||||||
switch {
|
|
||||||
case terminalSize.Width >= 120:
|
|
||||||
limit = 120
|
|
||||||
case terminalSize.Width >= 100:
|
|
||||||
limit = 100
|
|
||||||
case terminalSize.Width >= 80:
|
|
||||||
limit = 80
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewWordWrapWriter(w, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWordWrapWriter is a Writer that supports a limit of characters on every line
|
|
||||||
// and does auto word wrapping that respects that limit.
|
|
||||||
func NewWordWrapWriter(w io.Writer, limit uint) io.Writer {
|
|
||||||
return &wordWrapWriter{
|
|
||||||
limit: limit,
|
|
||||||
writer: w,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w wordWrapWriter) Write(p []byte) (nn int, err error) {
|
|
||||||
if w.limit == 0 {
|
|
||||||
return w.writer.Write(p)
|
|
||||||
}
|
|
||||||
original := string(p)
|
|
||||||
wrapped := wordwrap.WrapString(original, w.limit)
|
|
||||||
return w.writer.Write([]byte(wrapped))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPunchCardWriter is a NewWordWrapWriter that limits the line width to 80 columns.
|
|
||||||
func NewPunchCardWriter(w io.Writer) io.Writer {
|
|
||||||
return NewWordWrapWriter(w, 80)
|
|
||||||
}
|
|
||||||
|
|
||||||
type maxWidthWriter struct {
|
|
||||||
maxWidth uint
|
|
||||||
currentWidth uint
|
|
||||||
written uint
|
|
||||||
writer io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMaxWidthWriter is a Writer that supports a limit of characters on every
|
|
||||||
// line, but doesn't do any word wrapping automatically.
|
|
||||||
func NewMaxWidthWriter(w io.Writer, maxWidth uint) io.Writer {
|
|
||||||
return &maxWidthWriter{
|
|
||||||
maxWidth: maxWidth,
|
|
||||||
writer: w,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m maxWidthWriter) Write(p []byte) (nn int, err error) {
|
|
||||||
for _, b := range p {
|
|
||||||
if m.currentWidth == m.maxWidth {
|
|
||||||
m.writer.Write([]byte{'\n'})
|
|
||||||
m.currentWidth = 0
|
|
||||||
}
|
|
||||||
if b == '\n' {
|
|
||||||
m.currentWidth = 0
|
|
||||||
}
|
|
||||||
_, err := m.writer.Write([]byte{b})
|
|
||||||
if err != nil {
|
|
||||||
return int(m.written), err
|
|
||||||
}
|
|
||||||
m.written++
|
|
||||||
m.currentWidth++
|
|
||||||
}
|
|
||||||
return len(p), nil
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
// 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 term
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
const test = "Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam Iam"
|
|
||||||
|
|
||||||
func TestWordWrapWriter(t *testing.T) {
|
|
||||||
testcases := map[string]struct {
|
|
||||||
input string
|
|
||||||
maxWidth uint
|
|
||||||
}{
|
|
||||||
"max 10": {input: test, maxWidth: 10},
|
|
||||||
"max 80": {input: test, maxWidth: 80},
|
|
||||||
"max 120": {input: test, maxWidth: 120},
|
|
||||||
"max 5000": {input: test, maxWidth: 5000},
|
|
||||||
}
|
|
||||||
for k, tc := range testcases {
|
|
||||||
b := bytes.NewBufferString("")
|
|
||||||
w := NewWordWrapWriter(b, tc.maxWidth)
|
|
||||||
_, err := w.Write([]byte(tc.input))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: Unexpected error: %v", k, err)
|
|
||||||
}
|
|
||||||
result := b.String()
|
|
||||||
if !strings.Contains(result, "Iam") {
|
|
||||||
t.Errorf("%s: Expected to contain \"Iam\"", k)
|
|
||||||
}
|
|
||||||
if len(result) < len(tc.input) {
|
|
||||||
t.Errorf(
|
|
||||||
"%s: Unexpectedly short string, got %d wanted at least %d chars: %q",
|
|
||||||
k,
|
|
||||||
len(result),
|
|
||||||
len(tc.input),
|
|
||||||
result,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for _, line := range strings.Split(result, "\n") {
|
|
||||||
if len(line) > int(tc.maxWidth) {
|
|
||||||
t.Errorf("%s: Every line must be at most %d chars long, got %d: %q", k, tc.maxWidth, len(line), line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, word := range strings.Split(result, " ") {
|
|
||||||
if !strings.Contains(word, "Iam") {
|
|
||||||
t.Errorf("%s: Unexpected broken word: %q", k, word)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMaxWidthWriter(t *testing.T) {
|
|
||||||
testcases := map[string]struct {
|
|
||||||
input string
|
|
||||||
maxWidth uint
|
|
||||||
}{
|
|
||||||
"max 10": {input: test, maxWidth: 10},
|
|
||||||
"max 80": {input: test, maxWidth: 80},
|
|
||||||
"max 120": {input: test, maxWidth: 120},
|
|
||||||
"max 5000": {input: test, maxWidth: 5000},
|
|
||||||
}
|
|
||||||
for k, tc := range testcases {
|
|
||||||
b := bytes.NewBufferString("")
|
|
||||||
w := NewMaxWidthWriter(b, tc.maxWidth)
|
|
||||||
_, err := w.Write([]byte(tc.input))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: Unexpected error: %v", k, err)
|
|
||||||
}
|
|
||||||
result := b.String()
|
|
||||||
if !strings.Contains(result, "Iam") {
|
|
||||||
t.Errorf("%s: Expected to contain \"Iam\"", k)
|
|
||||||
}
|
|
||||||
if len(result) < len(tc.input) {
|
|
||||||
t.Errorf(
|
|
||||||
"%s: Unexpectedly short string, got %d wanted at least %d chars: %q",
|
|
||||||
k,
|
|
||||||
len(result),
|
|
||||||
len(tc.input),
|
|
||||||
result,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
lines := strings.Split(result, "\n")
|
|
||||||
for i, line := range lines {
|
|
||||||
if len(line) > int(tc.maxWidth) {
|
|
||||||
t.Errorf("%s: Every line must be at most %d chars long, got %d: %q", k, tc.maxWidth, len(line), line)
|
|
||||||
}
|
|
||||||
if i < len(lines)-1 && len(line) != int(tc.maxWidth) {
|
|
||||||
t.Errorf(
|
|
||||||
"%s: Lines except the last one are expected to be exactly %d chars long, got %d: %q",
|
|
||||||
k,
|
|
||||||
tc.maxWidth,
|
|
||||||
len(line),
|
|
||||||
line,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in new issue