diff --git a/api/api_config.gen.go b/api/api_config.gen.go new file mode 100644 index 0000000..841c88e --- /dev/null +++ b/api/api_config.gen.go @@ -0,0 +1,237 @@ +/* + * Subset of Influx API covered by Influx CLI + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 2.0.0 + */ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + _context "context" + _fmt "fmt" + _io "io" + _nethttp "net/http" + _neturl "net/url" +) + +// Linger please +var ( + _ _context.Context +) + +type ConfigApi interface { + + /* + * GetConfig Get the run-time configuration of the instance + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @return ApiGetConfigRequest + */ + GetConfig(ctx _context.Context) ApiGetConfigRequest + + /* + * GetConfigExecute executes the request + * @return Config + */ + GetConfigExecute(r ApiGetConfigRequest) (Config, error) + + /* + * GetConfigExecuteWithHttpInfo executes the request with HTTP response info returned. The response body is not + * available on the returned HTTP response as it will have already been read and closed; access to the response body + * content should be achieved through the returned response model if applicable. + * @return Config + */ + GetConfigExecuteWithHttpInfo(r ApiGetConfigRequest) (Config, *_nethttp.Response, error) + + // Sets additional descriptive text in the error message if any request in + // this API fails, indicating that it is intended to be used only on OSS + // servers. + OnlyOSS() ConfigApi + + // Sets additional descriptive text in the error message if any request in + // this API fails, indicating that it is intended to be used only on cloud + // servers. + OnlyCloud() ConfigApi +} + +// ConfigApiService ConfigApi service +type ConfigApiService service + +func (a *ConfigApiService) OnlyOSS() ConfigApi { + a.isOnlyOSS = true + return a +} + +func (a *ConfigApiService) OnlyCloud() ConfigApi { + a.isOnlyCloud = true + return a +} + +type ApiGetConfigRequest struct { + ctx _context.Context + ApiService ConfigApi + zapTraceSpan *string +} + +func (r ApiGetConfigRequest) ZapTraceSpan(zapTraceSpan string) ApiGetConfigRequest { + r.zapTraceSpan = &zapTraceSpan + return r +} +func (r ApiGetConfigRequest) GetZapTraceSpan() *string { + return r.zapTraceSpan +} + +func (r ApiGetConfigRequest) Execute() (Config, error) { + return r.ApiService.GetConfigExecute(r) +} + +func (r ApiGetConfigRequest) ExecuteWithHttpInfo() (Config, *_nethttp.Response, error) { + return r.ApiService.GetConfigExecuteWithHttpInfo(r) +} + +/* + * GetConfig Get the run-time configuration of the instance + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @return ApiGetConfigRequest + */ +func (a *ConfigApiService) GetConfig(ctx _context.Context) ApiGetConfigRequest { + return ApiGetConfigRequest{ + ApiService: a, + ctx: ctx, + } +} + +/* + * Execute executes the request + * @return Config + */ +func (a *ConfigApiService) GetConfigExecute(r ApiGetConfigRequest) (Config, error) { + returnVal, _, err := a.GetConfigExecuteWithHttpInfo(r) + return returnVal, err +} + +/* + * ExecuteWithHttpInfo executes the request with HTTP response info returned. The response body is not available on the + * returned HTTP response as it will have already been read and closed; access to the response body content should be + * achieved through the returned response model if applicable. + * @return Config + */ +func (a *ConfigApiService) GetConfigExecuteWithHttpInfo(r ApiGetConfigRequest) (Config, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodGet + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue Config + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ConfigApiService.GetConfig") + if err != nil { + return localVarReturnValue, nil, GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/config" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.zapTraceSpan != nil { + localVarHeaderParams["Zap-Trace-Span"] = parameterToString(*r.zapTraceSpan, "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + var errorPrefix string + if a.isOnlyOSS { + errorPrefix = "InfluxDB OSS-only command failed: " + } else if a.isOnlyCloud { + errorPrefix = "InfluxDB Cloud-only command failed: " + } + + if localVarHTTPResponse.StatusCode >= 300 { + body, err := GunzipIfNeeded(localVarHTTPResponse) + if err != nil { + body.Close() + return localVarReturnValue, localVarHTTPResponse, _fmt.Errorf("%s%w", errorPrefix, err) + } + localVarBody, err := _io.ReadAll(body) + body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, _fmt.Errorf("%s%w", errorPrefix, err) + } + newErr := GenericOpenAPIError{ + body: localVarBody, + error: _fmt.Sprintf("%s%s", errorPrefix, localVarHTTPResponse.Status), + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = _fmt.Sprintf("%s: %s", newErr.Error(), err.Error()) + return localVarReturnValue, localVarHTTPResponse, newErr + } + v.SetMessage(_fmt.Sprintf("%s: %s", newErr.Error(), v.GetMessage())) + newErr.model = &v + return localVarReturnValue, localVarHTTPResponse, newErr + } + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = _fmt.Sprintf("%s: %s", newErr.Error(), err.Error()) + return localVarReturnValue, localVarHTTPResponse, newErr + } + v.SetMessage(_fmt.Sprintf("%s: %s", newErr.Error(), v.GetMessage())) + newErr.model = &v + return localVarReturnValue, localVarHTTPResponse, newErr + } + + body, err := GunzipIfNeeded(localVarHTTPResponse) + if err != nil { + body.Close() + return localVarReturnValue, localVarHTTPResponse, _fmt.Errorf("%s%w", errorPrefix, err) + } + localVarBody, err := _io.ReadAll(body) + body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, _fmt.Errorf("%s%w", errorPrefix, err) + } + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: _fmt.Sprintf("%s%s", errorPrefix, err.Error()), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/api/client.gen.go b/api/client.gen.go index 35a7464..5e3ae9a 100644 --- a/api/client.gen.go +++ b/api/client.gen.go @@ -54,6 +54,8 @@ type APIClient struct { BucketsApi BucketsApi + ConfigApi ConfigApi + DBRPsApi DBRPsApi DashboardsApi DashboardsApi @@ -115,6 +117,7 @@ func NewAPIClient(cfg *Configuration) *APIClient { c.BackupApi = (*BackupApiService)(&c.common) c.BucketSchemasApi = (*BucketSchemasApiService)(&c.common) c.BucketsApi = (*BucketsApiService)(&c.common) + c.ConfigApi = (*ConfigApiService)(&c.common) c.DBRPsApi = (*DBRPsApiService)(&c.common) c.DashboardsApi = (*DashboardsApiService)(&c.common) c.DeleteApi = (*DeleteApiService)(&c.common) diff --git a/api/contract/cli.yml b/api/contract/cli.yml index bf0b3e9..acbbd35 100644 --- a/api/contract/cli.yml +++ b/api/contract/cli.yml @@ -119,6 +119,8 @@ paths: $ref: "./openapi/src/oss/paths/replications_replicationID.yml" /replications/{replicationID}/validate: $ref: "./openapi/src/oss/paths/replications_replicationID_validate.yml" + /config: + $ref: "./openapi/src/oss/paths/config.yml" components: parameters: TraceSpan: @@ -449,3 +451,5 @@ components: $ref: "./openapi/src/oss/schemas/ReplicationUpdateRequest.yml" Replications: $ref: "./openapi/src/oss/schemas/Replications.yml" + Config: + $ref: "./openapi/src/oss/schemas/Config.yml" diff --git a/api/contract/openapi b/api/contract/openapi index 2199c99..8b5f1bb 160000 --- a/api/contract/openapi +++ b/api/contract/openapi @@ -1 +1 @@ -Subproject commit 2199c9997e9fb159acc0cc524a1e5fa30d6229c2 +Subproject commit 8b5f1bbb2cd388eb454dc9da19e3d2c4061cdf5f diff --git a/api/model_config.gen.go b/api/model_config.gen.go new file mode 100644 index 0000000..b0660be --- /dev/null +++ b/api/model_config.gen.go @@ -0,0 +1,113 @@ +/* + * Subset of Influx API covered by Influx CLI + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 2.0.0 + */ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" +) + +// Config struct for Config +type Config struct { + Config *map[string]interface{} `json:"config,omitempty" yaml:"config,omitempty"` +} + +// NewConfig instantiates a new Config object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewConfig() *Config { + this := Config{} + return &this +} + +// NewConfigWithDefaults instantiates a new Config object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewConfigWithDefaults() *Config { + this := Config{} + return &this +} + +// GetConfig returns the Config field value if set, zero value otherwise. +func (o *Config) GetConfig() map[string]interface{} { + if o == nil || o.Config == nil { + var ret map[string]interface{} + return ret + } + return *o.Config +} + +// GetConfigOk returns a tuple with the Config field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Config) GetConfigOk() (*map[string]interface{}, bool) { + if o == nil || o.Config == nil { + return nil, false + } + return o.Config, true +} + +// HasConfig returns a boolean if a field has been set. +func (o *Config) HasConfig() bool { + if o != nil && o.Config != nil { + return true + } + + return false +} + +// SetConfig gets a reference to the given map[string]interface{} and assigns it to the Config field. +func (o *Config) SetConfig(v map[string]interface{}) { + o.Config = &v +} + +func (o Config) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if o.Config != nil { + toSerialize["config"] = o.Config + } + return json.Marshal(toSerialize) +} + +type NullableConfig struct { + value *Config + isSet bool +} + +func (v NullableConfig) Get() *Config { + return v.value +} + +func (v *NullableConfig) Set(val *Config) { + v.value = val + v.isSet = true +} + +func (v NullableConfig) IsSet() bool { + return v.isSet +} + +func (v *NullableConfig) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableConfig(val *Config) *NullableConfig { + return &NullableConfig{value: val, isSet: true} +} + +func (v NullableConfig) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableConfig) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/clients/server_config/server_config.go b/clients/server_config/server_config.go new file mode 100644 index 0000000..32193bc --- /dev/null +++ b/clients/server_config/server_config.go @@ -0,0 +1,47 @@ +package server_config + +import ( + "context" + "fmt" + + "github.com/BurntSushi/toml" + "github.com/influxdata/influx-cli/v2/api" + "github.com/influxdata/influx-cli/v2/clients" + "gopkg.in/yaml.v3" +) + +type Client struct { + clients.CLI + api.ConfigApi +} + +type ListParams struct { + TOML bool + YAML bool +} + +func (c Client) List(ctx context.Context, params *ListParams) error { + response, err := c.GetConfig(ctx).Execute() + if err != nil { + return fmt.Errorf("failed to retrieve config: %w", err) + } + + config := response.GetConfig() + + // Any numeric values that were decoded as floats need to be converted to + // integers. InfluxDB currently does not have any float config values, and + // this prevents formatting issues with the output. + for k, v := range config { + if f, ok := v.(float64); ok { + config[k] = int(f) + } + } + + if params.TOML { + return toml.NewEncoder(c.StdIO).Encode(config) + } else if params.YAML { + return yaml.NewEncoder(c.StdIO).Encode(config) + } + + return c.PrintJSON(config) +} diff --git a/cmd/influx/main.go b/cmd/influx/main.go index 499ab9c..ca44772 100644 --- a/cmd/influx/main.go +++ b/cmd/influx/main.go @@ -57,6 +57,7 @@ var app = cli.App{ newTemplateCmd(), newRemoteCmd(), newReplicationCmd(), + newServerConfigCommand(), }, Before: middleware.WithBeforeFns(withContext(), middleware.NoArgs), } diff --git a/cmd/influx/server_config.go b/cmd/influx/server_config.go new file mode 100644 index 0000000..d7267b5 --- /dev/null +++ b/cmd/influx/server_config.go @@ -0,0 +1,43 @@ +package main + +import ( + "errors" + + "github.com/influxdata/influx-cli/v2/clients/server_config" + "github.com/influxdata/influx-cli/v2/pkg/cli/middleware" + "github.com/urfave/cli" +) + +func newServerConfigCommand() cli.Command { + var params server_config.ListParams + return cli.Command{ + Name: "server-config", + Usage: "Display server config", + Flags: append( + commonFlags(), + &cli.BoolFlag{ + Name: "toml", + Usage: "Output configuration as TOML instead of JSON", + Destination: ¶ms.TOML, + }, + &cli.BoolFlag{ + Name: "yaml", + Usage: "Output configuration as YAML instead of JSON", + Destination: ¶ms.YAML, + }, + ), + Before: middleware.WithBeforeFns(withCli(), withApi(true), middleware.NoArgs), + Action: func(ctx *cli.Context) error { + if params.TOML && params.YAML { + return errors.New("cannot specify both TOML and YAML simultaneously") + } + + api := getAPI(ctx) + client := server_config.Client{ + CLI: getCLI(ctx), + ConfigApi: api.ConfigApi, + } + return client.List(getContext(ctx), ¶ms) + }, + } +}