diff --git a/api/api_templates.gen.go b/api/api_templates.gen.go new file mode 100644 index 0000000..23ae956 --- /dev/null +++ b/api/api_templates.gen.go @@ -0,0 +1,173 @@ +/* + * 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" + _ioutil "io/ioutil" + _nethttp "net/http" + _neturl "net/url" +) + +// Linger please +var ( + _ _context.Context +) + +type TemplatesApi interface { + + /* + * ExportTemplate Export a new Influx Template + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @return ApiExportTemplateRequest + */ + ExportTemplate(ctx _context.Context) ApiExportTemplateRequest + + /* + * ExportTemplateExecute executes the request + * @return []TemplateEntry + */ + ExportTemplateExecute(r ApiExportTemplateRequest) ([]TemplateEntry, error) +} + +// TemplatesApiService TemplatesApi service +type TemplatesApiService service + +type ApiExportTemplateRequest struct { + ctx _context.Context + ApiService TemplatesApi + templateExport *TemplateExport +} + +func (r ApiExportTemplateRequest) TemplateExport(templateExport TemplateExport) ApiExportTemplateRequest { + r.templateExport = &templateExport + return r +} +func (r ApiExportTemplateRequest) GetTemplateExport() *TemplateExport { + return r.templateExport +} + +func (r ApiExportTemplateRequest) Execute() ([]TemplateEntry, error) { + return r.ApiService.ExportTemplateExecute(r) +} + +/* + * ExportTemplate Export a new Influx Template + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @return ApiExportTemplateRequest + */ +func (a *TemplatesApiService) ExportTemplate(ctx _context.Context) ApiExportTemplateRequest { + return ApiExportTemplateRequest{ + ApiService: a, + ctx: ctx, + } +} + +/* + * Execute executes the request + * @return []TemplateEntry + */ +func (a *TemplatesApiService) ExportTemplateExecute(r ApiExportTemplateRequest) ([]TemplateEntry, error) { + var ( + localVarHTTPMethod = _nethttp.MethodPost + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []TemplateEntry + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "TemplatesApiService.ExportTemplate") + if err != nil { + return localVarReturnValue, GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/templates/export" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // 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 + } + // body params + localVarPostBody = r.templateExport + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + body, err := GunzipIfNeeded(localVarHTTPResponse) + if err != nil { + body.Close() + return localVarReturnValue, err + } + localVarBody, err := _ioutil.ReadAll(body) + body.Close() + if err != nil { + return localVarReturnValue, err + } + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, newErr + } + newErr.model = &v + return localVarReturnValue, newErr + } + + body, err := GunzipIfNeeded(localVarHTTPResponse) + if err != nil { + body.Close() + return localVarReturnValue, err + } + localVarBody, err := _ioutil.ReadAll(body) + body.Close() + if err != nil { + return localVarReturnValue, err + } + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, newErr + } + + return localVarReturnValue, nil +} diff --git a/api/client.gen.go b/api/client.gen.go index 0fdf833..2b9ea9d 100644 --- a/api/client.gen.go +++ b/api/client.gen.go @@ -71,6 +71,8 @@ type APIClient struct { TelegrafsApi TelegrafsApi + TemplatesApi TemplatesApi + UsersApi UsersApi WriteApi WriteApi @@ -104,6 +106,7 @@ func NewAPIClient(cfg *Configuration) *APIClient { c.SetupApi = (*SetupApiService)(&c.common) c.TasksApi = (*TasksApiService)(&c.common) c.TelegrafsApi = (*TelegrafsApiService)(&c.common) + c.TemplatesApi = (*TemplatesApiService)(&c.common) c.UsersApi = (*UsersApiService)(&c.common) c.WriteApi = (*WriteApiService)(&c.common) diff --git a/api/contract/cli.yml b/api/contract/cli.yml index 7fc19e7..3a58886 100644 --- a/api/contract/cli.yml +++ b/api/contract/cli.yml @@ -71,6 +71,8 @@ paths: $ref: "./overrides/paths/telegrafs_telegrafID.yml" /dashboards: $ref: "./overrides/paths/dashboards.yml" + /templates/export: + $ref: "./overrides/paths/templates_export.yml" components: parameters: TraceSpan: @@ -253,3 +255,11 @@ components: $ref: "./openapi/src/common/schemas/Cells.yml" Cell: $ref: "./openapi/src/common/schemas/Cell.yml" + TemplateExport: + $ref: "./overrides/schemas/TemplateExport.yml" + Template: + $ref: "./openapi/src/common/schemas/Template.yml" + TemplateKind: + $ref: "./openapi/src/common/schemas/TemplateKind.yml" + TemplateEntry: + $ref: "./openapi/src/common/schemas/TemplateEntry.yml" diff --git a/api/contract/openapi b/api/contract/openapi index b837e21..1060f69 160000 --- a/api/contract/openapi +++ b/api/contract/openapi @@ -1 +1 @@ -Subproject commit b837e212d51bfd9510a486849678c8a8e38e1d8e +Subproject commit 1060f69e9ffd3307a6db63c817cd51a82ff73fc4 diff --git a/api/contract/overrides/paths/templates_export.yml b/api/contract/overrides/paths/templates_export.yml new file mode 100644 index 0000000..7501f0f --- /dev/null +++ b/api/contract/overrides/paths/templates_export.yml @@ -0,0 +1,25 @@ +post: + operationId: ExportTemplate + tags: + - Templates + summary: Export a new Influx Template + requestBody: + description: Export resources as an InfluxDB template. + required: false + content: + application/json: + schema: + $ref: "../schemas/TemplateExport.yml" + responses: + "200": + description: InfluxDB template created + content: + application/json: + schema: + $ref: "../../openapi/src/common/schemas/Template.yml" + default: + description: Unexpected error + content: + application/json: + schema: + $ref: "../../openapi/src/common/schemas/Error.yml" diff --git a/api/contract/overrides/schemas/TemplateExport.yml b/api/contract/overrides/schemas/TemplateExport.yml new file mode 100644 index 0000000..e47e21f --- /dev/null +++ b/api/contract/overrides/schemas/TemplateExport.yml @@ -0,0 +1,35 @@ +type: object +properties: + stackID: + type: string + orgIDs: + type: array + items: + type: object + properties: + orgID: + type: string + resourceFilters: + type: object + properties: + byLabel: + type: array + items: + type: string + byResourceKind: + type: array + items: + $ref: "../../openapi/src/common/schemas/TemplateKind.yml" + resources: + type: array + items: + type: object + properties: + id: + type: string + kind: + $ref: "../../openapi/src/common/schemas/TemplateKind.yml" + name: + type: string + required: [kind] +required: [resources] diff --git a/api/model_template_entry.gen.go b/api/model_template_entry.gen.go new file mode 100644 index 0000000..60fcb07 --- /dev/null +++ b/api/model_template_entry.gen.go @@ -0,0 +1,221 @@ +/* + * 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" +) + +// TemplateEntry struct for TemplateEntry +type TemplateEntry struct { + ApiVersion *string `json:"apiVersion,omitempty"` + Kind *TemplateKind `json:"kind,omitempty"` + Meta *TemplateEntryMeta `json:"meta,omitempty"` + Spec *map[string]interface{} `json:"spec,omitempty"` +} + +// NewTemplateEntry instantiates a new TemplateEntry 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 NewTemplateEntry() *TemplateEntry { + this := TemplateEntry{} + return &this +} + +// NewTemplateEntryWithDefaults instantiates a new TemplateEntry 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 NewTemplateEntryWithDefaults() *TemplateEntry { + this := TemplateEntry{} + return &this +} + +// GetApiVersion returns the ApiVersion field value if set, zero value otherwise. +func (o *TemplateEntry) GetApiVersion() string { + if o == nil || o.ApiVersion == nil { + var ret string + return ret + } + return *o.ApiVersion +} + +// GetApiVersionOk returns a tuple with the ApiVersion field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateEntry) GetApiVersionOk() (*string, bool) { + if o == nil || o.ApiVersion == nil { + return nil, false + } + return o.ApiVersion, true +} + +// HasApiVersion returns a boolean if a field has been set. +func (o *TemplateEntry) HasApiVersion() bool { + if o != nil && o.ApiVersion != nil { + return true + } + + return false +} + +// SetApiVersion gets a reference to the given string and assigns it to the ApiVersion field. +func (o *TemplateEntry) SetApiVersion(v string) { + o.ApiVersion = &v +} + +// GetKind returns the Kind field value if set, zero value otherwise. +func (o *TemplateEntry) GetKind() TemplateKind { + if o == nil || o.Kind == nil { + var ret TemplateKind + return ret + } + return *o.Kind +} + +// GetKindOk returns a tuple with the Kind field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateEntry) GetKindOk() (*TemplateKind, bool) { + if o == nil || o.Kind == nil { + return nil, false + } + return o.Kind, true +} + +// HasKind returns a boolean if a field has been set. +func (o *TemplateEntry) HasKind() bool { + if o != nil && o.Kind != nil { + return true + } + + return false +} + +// SetKind gets a reference to the given TemplateKind and assigns it to the Kind field. +func (o *TemplateEntry) SetKind(v TemplateKind) { + o.Kind = &v +} + +// GetMeta returns the Meta field value if set, zero value otherwise. +func (o *TemplateEntry) GetMeta() TemplateEntryMeta { + if o == nil || o.Meta == nil { + var ret TemplateEntryMeta + return ret + } + return *o.Meta +} + +// GetMetaOk returns a tuple with the Meta field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateEntry) GetMetaOk() (*TemplateEntryMeta, bool) { + if o == nil || o.Meta == nil { + return nil, false + } + return o.Meta, true +} + +// HasMeta returns a boolean if a field has been set. +func (o *TemplateEntry) HasMeta() bool { + if o != nil && o.Meta != nil { + return true + } + + return false +} + +// SetMeta gets a reference to the given TemplateEntryMeta and assigns it to the Meta field. +func (o *TemplateEntry) SetMeta(v TemplateEntryMeta) { + o.Meta = &v +} + +// GetSpec returns the Spec field value if set, zero value otherwise. +func (o *TemplateEntry) GetSpec() map[string]interface{} { + if o == nil || o.Spec == nil { + var ret map[string]interface{} + return ret + } + return *o.Spec +} + +// GetSpecOk returns a tuple with the Spec field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateEntry) GetSpecOk() (*map[string]interface{}, bool) { + if o == nil || o.Spec == nil { + return nil, false + } + return o.Spec, true +} + +// HasSpec returns a boolean if a field has been set. +func (o *TemplateEntry) HasSpec() bool { + if o != nil && o.Spec != nil { + return true + } + + return false +} + +// SetSpec gets a reference to the given map[string]interface{} and assigns it to the Spec field. +func (o *TemplateEntry) SetSpec(v map[string]interface{}) { + o.Spec = &v +} + +func (o TemplateEntry) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if o.ApiVersion != nil { + toSerialize["apiVersion"] = o.ApiVersion + } + if o.Kind != nil { + toSerialize["kind"] = o.Kind + } + if o.Meta != nil { + toSerialize["meta"] = o.Meta + } + if o.Spec != nil { + toSerialize["spec"] = o.Spec + } + return json.Marshal(toSerialize) +} + +type NullableTemplateEntry struct { + value *TemplateEntry + isSet bool +} + +func (v NullableTemplateEntry) Get() *TemplateEntry { + return v.value +} + +func (v *NullableTemplateEntry) Set(val *TemplateEntry) { + v.value = val + v.isSet = true +} + +func (v NullableTemplateEntry) IsSet() bool { + return v.isSet +} + +func (v *NullableTemplateEntry) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableTemplateEntry(val *TemplateEntry) *NullableTemplateEntry { + return &NullableTemplateEntry{value: val, isSet: true} +} + +func (v NullableTemplateEntry) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableTemplateEntry) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/api/model_template_entry_meta.gen.go b/api/model_template_entry_meta.gen.go new file mode 100644 index 0000000..b679fff --- /dev/null +++ b/api/model_template_entry_meta.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" +) + +// TemplateEntryMeta struct for TemplateEntryMeta +type TemplateEntryMeta struct { + Name *string `json:"name,omitempty"` +} + +// NewTemplateEntryMeta instantiates a new TemplateEntryMeta 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 NewTemplateEntryMeta() *TemplateEntryMeta { + this := TemplateEntryMeta{} + return &this +} + +// NewTemplateEntryMetaWithDefaults instantiates a new TemplateEntryMeta 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 NewTemplateEntryMetaWithDefaults() *TemplateEntryMeta { + this := TemplateEntryMeta{} + return &this +} + +// GetName returns the Name field value if set, zero value otherwise. +func (o *TemplateEntryMeta) GetName() string { + if o == nil || o.Name == nil { + var ret string + return ret + } + return *o.Name +} + +// GetNameOk returns a tuple with the Name field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateEntryMeta) GetNameOk() (*string, bool) { + if o == nil || o.Name == nil { + return nil, false + } + return o.Name, true +} + +// HasName returns a boolean if a field has been set. +func (o *TemplateEntryMeta) HasName() bool { + if o != nil && o.Name != nil { + return true + } + + return false +} + +// SetName gets a reference to the given string and assigns it to the Name field. +func (o *TemplateEntryMeta) SetName(v string) { + o.Name = &v +} + +func (o TemplateEntryMeta) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if o.Name != nil { + toSerialize["name"] = o.Name + } + return json.Marshal(toSerialize) +} + +type NullableTemplateEntryMeta struct { + value *TemplateEntryMeta + isSet bool +} + +func (v NullableTemplateEntryMeta) Get() *TemplateEntryMeta { + return v.value +} + +func (v *NullableTemplateEntryMeta) Set(val *TemplateEntryMeta) { + v.value = val + v.isSet = true +} + +func (v NullableTemplateEntryMeta) IsSet() bool { + return v.isSet +} + +func (v *NullableTemplateEntryMeta) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableTemplateEntryMeta(val *TemplateEntryMeta) *NullableTemplateEntryMeta { + return &NullableTemplateEntryMeta{value: val, isSet: true} +} + +func (v NullableTemplateEntryMeta) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableTemplateEntryMeta) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/api/model_template_export.gen.go b/api/model_template_export.gen.go new file mode 100644 index 0000000..21130a9 --- /dev/null +++ b/api/model_template_export.gen.go @@ -0,0 +1,178 @@ +/* + * 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" +) + +// TemplateExport struct for TemplateExport +type TemplateExport struct { + StackID *string `json:"stackID,omitempty"` + OrgIDs *[]TemplateExportOrgIDs `json:"orgIDs,omitempty"` + Resources []TemplateExportResources `json:"resources"` +} + +// NewTemplateExport instantiates a new TemplateExport 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 NewTemplateExport(resources []TemplateExportResources) *TemplateExport { + this := TemplateExport{} + this.Resources = resources + return &this +} + +// NewTemplateExportWithDefaults instantiates a new TemplateExport 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 NewTemplateExportWithDefaults() *TemplateExport { + this := TemplateExport{} + return &this +} + +// GetStackID returns the StackID field value if set, zero value otherwise. +func (o *TemplateExport) GetStackID() string { + if o == nil || o.StackID == nil { + var ret string + return ret + } + return *o.StackID +} + +// GetStackIDOk returns a tuple with the StackID field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateExport) GetStackIDOk() (*string, bool) { + if o == nil || o.StackID == nil { + return nil, false + } + return o.StackID, true +} + +// HasStackID returns a boolean if a field has been set. +func (o *TemplateExport) HasStackID() bool { + if o != nil && o.StackID != nil { + return true + } + + return false +} + +// SetStackID gets a reference to the given string and assigns it to the StackID field. +func (o *TemplateExport) SetStackID(v string) { + o.StackID = &v +} + +// GetOrgIDs returns the OrgIDs field value if set, zero value otherwise. +func (o *TemplateExport) GetOrgIDs() []TemplateExportOrgIDs { + if o == nil || o.OrgIDs == nil { + var ret []TemplateExportOrgIDs + return ret + } + return *o.OrgIDs +} + +// GetOrgIDsOk returns a tuple with the OrgIDs field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateExport) GetOrgIDsOk() (*[]TemplateExportOrgIDs, bool) { + if o == nil || o.OrgIDs == nil { + return nil, false + } + return o.OrgIDs, true +} + +// HasOrgIDs returns a boolean if a field has been set. +func (o *TemplateExport) HasOrgIDs() bool { + if o != nil && o.OrgIDs != nil { + return true + } + + return false +} + +// SetOrgIDs gets a reference to the given []TemplateExportOrgIDs and assigns it to the OrgIDs field. +func (o *TemplateExport) SetOrgIDs(v []TemplateExportOrgIDs) { + o.OrgIDs = &v +} + +// GetResources returns the Resources field value +func (o *TemplateExport) GetResources() []TemplateExportResources { + if o == nil { + var ret []TemplateExportResources + return ret + } + + return o.Resources +} + +// GetResourcesOk returns a tuple with the Resources field value +// and a boolean to check if the value has been set. +func (o *TemplateExport) GetResourcesOk() (*[]TemplateExportResources, bool) { + if o == nil { + return nil, false + } + return &o.Resources, true +} + +// SetResources sets field value +func (o *TemplateExport) SetResources(v []TemplateExportResources) { + o.Resources = v +} + +func (o TemplateExport) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if o.StackID != nil { + toSerialize["stackID"] = o.StackID + } + if o.OrgIDs != nil { + toSerialize["orgIDs"] = o.OrgIDs + } + if true { + toSerialize["resources"] = o.Resources + } + return json.Marshal(toSerialize) +} + +type NullableTemplateExport struct { + value *TemplateExport + isSet bool +} + +func (v NullableTemplateExport) Get() *TemplateExport { + return v.value +} + +func (v *NullableTemplateExport) Set(val *TemplateExport) { + v.value = val + v.isSet = true +} + +func (v NullableTemplateExport) IsSet() bool { + return v.isSet +} + +func (v *NullableTemplateExport) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableTemplateExport(val *TemplateExport) *NullableTemplateExport { + return &NullableTemplateExport{value: val, isSet: true} +} + +func (v NullableTemplateExport) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableTemplateExport) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/api/model_template_export_org_ids.gen.go b/api/model_template_export_org_ids.gen.go new file mode 100644 index 0000000..75a72eb --- /dev/null +++ b/api/model_template_export_org_ids.gen.go @@ -0,0 +1,149 @@ +/* + * 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" +) + +// TemplateExportOrgIDs struct for TemplateExportOrgIDs +type TemplateExportOrgIDs struct { + OrgID *string `json:"orgID,omitempty"` + ResourceFilters *TemplateExportResourceFilters `json:"resourceFilters,omitempty"` +} + +// NewTemplateExportOrgIDs instantiates a new TemplateExportOrgIDs 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 NewTemplateExportOrgIDs() *TemplateExportOrgIDs { + this := TemplateExportOrgIDs{} + return &this +} + +// NewTemplateExportOrgIDsWithDefaults instantiates a new TemplateExportOrgIDs 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 NewTemplateExportOrgIDsWithDefaults() *TemplateExportOrgIDs { + this := TemplateExportOrgIDs{} + return &this +} + +// GetOrgID returns the OrgID field value if set, zero value otherwise. +func (o *TemplateExportOrgIDs) GetOrgID() string { + if o == nil || o.OrgID == nil { + var ret string + return ret + } + return *o.OrgID +} + +// GetOrgIDOk returns a tuple with the OrgID field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateExportOrgIDs) GetOrgIDOk() (*string, bool) { + if o == nil || o.OrgID == nil { + return nil, false + } + return o.OrgID, true +} + +// HasOrgID returns a boolean if a field has been set. +func (o *TemplateExportOrgIDs) HasOrgID() bool { + if o != nil && o.OrgID != nil { + return true + } + + return false +} + +// SetOrgID gets a reference to the given string and assigns it to the OrgID field. +func (o *TemplateExportOrgIDs) SetOrgID(v string) { + o.OrgID = &v +} + +// GetResourceFilters returns the ResourceFilters field value if set, zero value otherwise. +func (o *TemplateExportOrgIDs) GetResourceFilters() TemplateExportResourceFilters { + if o == nil || o.ResourceFilters == nil { + var ret TemplateExportResourceFilters + return ret + } + return *o.ResourceFilters +} + +// GetResourceFiltersOk returns a tuple with the ResourceFilters field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateExportOrgIDs) GetResourceFiltersOk() (*TemplateExportResourceFilters, bool) { + if o == nil || o.ResourceFilters == nil { + return nil, false + } + return o.ResourceFilters, true +} + +// HasResourceFilters returns a boolean if a field has been set. +func (o *TemplateExportOrgIDs) HasResourceFilters() bool { + if o != nil && o.ResourceFilters != nil { + return true + } + + return false +} + +// SetResourceFilters gets a reference to the given TemplateExportResourceFilters and assigns it to the ResourceFilters field. +func (o *TemplateExportOrgIDs) SetResourceFilters(v TemplateExportResourceFilters) { + o.ResourceFilters = &v +} + +func (o TemplateExportOrgIDs) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if o.OrgID != nil { + toSerialize["orgID"] = o.OrgID + } + if o.ResourceFilters != nil { + toSerialize["resourceFilters"] = o.ResourceFilters + } + return json.Marshal(toSerialize) +} + +type NullableTemplateExportOrgIDs struct { + value *TemplateExportOrgIDs + isSet bool +} + +func (v NullableTemplateExportOrgIDs) Get() *TemplateExportOrgIDs { + return v.value +} + +func (v *NullableTemplateExportOrgIDs) Set(val *TemplateExportOrgIDs) { + v.value = val + v.isSet = true +} + +func (v NullableTemplateExportOrgIDs) IsSet() bool { + return v.isSet +} + +func (v *NullableTemplateExportOrgIDs) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableTemplateExportOrgIDs(val *TemplateExportOrgIDs) *NullableTemplateExportOrgIDs { + return &NullableTemplateExportOrgIDs{value: val, isSet: true} +} + +func (v NullableTemplateExportOrgIDs) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableTemplateExportOrgIDs) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/api/model_template_export_resource_filters.gen.go b/api/model_template_export_resource_filters.gen.go new file mode 100644 index 0000000..85d9c05 --- /dev/null +++ b/api/model_template_export_resource_filters.gen.go @@ -0,0 +1,149 @@ +/* + * 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" +) + +// TemplateExportResourceFilters struct for TemplateExportResourceFilters +type TemplateExportResourceFilters struct { + ByLabel *[]string `json:"byLabel,omitempty"` + ByResourceKind *[]TemplateKind `json:"byResourceKind,omitempty"` +} + +// NewTemplateExportResourceFilters instantiates a new TemplateExportResourceFilters 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 NewTemplateExportResourceFilters() *TemplateExportResourceFilters { + this := TemplateExportResourceFilters{} + return &this +} + +// NewTemplateExportResourceFiltersWithDefaults instantiates a new TemplateExportResourceFilters 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 NewTemplateExportResourceFiltersWithDefaults() *TemplateExportResourceFilters { + this := TemplateExportResourceFilters{} + return &this +} + +// GetByLabel returns the ByLabel field value if set, zero value otherwise. +func (o *TemplateExportResourceFilters) GetByLabel() []string { + if o == nil || o.ByLabel == nil { + var ret []string + return ret + } + return *o.ByLabel +} + +// GetByLabelOk returns a tuple with the ByLabel field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateExportResourceFilters) GetByLabelOk() (*[]string, bool) { + if o == nil || o.ByLabel == nil { + return nil, false + } + return o.ByLabel, true +} + +// HasByLabel returns a boolean if a field has been set. +func (o *TemplateExportResourceFilters) HasByLabel() bool { + if o != nil && o.ByLabel != nil { + return true + } + + return false +} + +// SetByLabel gets a reference to the given []string and assigns it to the ByLabel field. +func (o *TemplateExportResourceFilters) SetByLabel(v []string) { + o.ByLabel = &v +} + +// GetByResourceKind returns the ByResourceKind field value if set, zero value otherwise. +func (o *TemplateExportResourceFilters) GetByResourceKind() []TemplateKind { + if o == nil || o.ByResourceKind == nil { + var ret []TemplateKind + return ret + } + return *o.ByResourceKind +} + +// GetByResourceKindOk returns a tuple with the ByResourceKind field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateExportResourceFilters) GetByResourceKindOk() (*[]TemplateKind, bool) { + if o == nil || o.ByResourceKind == nil { + return nil, false + } + return o.ByResourceKind, true +} + +// HasByResourceKind returns a boolean if a field has been set. +func (o *TemplateExportResourceFilters) HasByResourceKind() bool { + if o != nil && o.ByResourceKind != nil { + return true + } + + return false +} + +// SetByResourceKind gets a reference to the given []TemplateKind and assigns it to the ByResourceKind field. +func (o *TemplateExportResourceFilters) SetByResourceKind(v []TemplateKind) { + o.ByResourceKind = &v +} + +func (o TemplateExportResourceFilters) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if o.ByLabel != nil { + toSerialize["byLabel"] = o.ByLabel + } + if o.ByResourceKind != nil { + toSerialize["byResourceKind"] = o.ByResourceKind + } + return json.Marshal(toSerialize) +} + +type NullableTemplateExportResourceFilters struct { + value *TemplateExportResourceFilters + isSet bool +} + +func (v NullableTemplateExportResourceFilters) Get() *TemplateExportResourceFilters { + return v.value +} + +func (v *NullableTemplateExportResourceFilters) Set(val *TemplateExportResourceFilters) { + v.value = val + v.isSet = true +} + +func (v NullableTemplateExportResourceFilters) IsSet() bool { + return v.isSet +} + +func (v *NullableTemplateExportResourceFilters) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableTemplateExportResourceFilters(val *TemplateExportResourceFilters) *NullableTemplateExportResourceFilters { + return &NullableTemplateExportResourceFilters{value: val, isSet: true} +} + +func (v NullableTemplateExportResourceFilters) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableTemplateExportResourceFilters) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/api/model_template_export_resources.gen.go b/api/model_template_export_resources.gen.go new file mode 100644 index 0000000..cc60fa8 --- /dev/null +++ b/api/model_template_export_resources.gen.go @@ -0,0 +1,178 @@ +/* + * 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" +) + +// TemplateExportResources struct for TemplateExportResources +type TemplateExportResources struct { + Id *string `json:"id,omitempty"` + Kind TemplateKind `json:"kind"` + Name *string `json:"name,omitempty"` +} + +// NewTemplateExportResources instantiates a new TemplateExportResources 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 NewTemplateExportResources(kind TemplateKind) *TemplateExportResources { + this := TemplateExportResources{} + this.Kind = kind + return &this +} + +// NewTemplateExportResourcesWithDefaults instantiates a new TemplateExportResources 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 NewTemplateExportResourcesWithDefaults() *TemplateExportResources { + this := TemplateExportResources{} + return &this +} + +// GetId returns the Id field value if set, zero value otherwise. +func (o *TemplateExportResources) GetId() string { + if o == nil || o.Id == nil { + var ret string + return ret + } + return *o.Id +} + +// GetIdOk returns a tuple with the Id field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateExportResources) GetIdOk() (*string, bool) { + if o == nil || o.Id == nil { + return nil, false + } + return o.Id, true +} + +// HasId returns a boolean if a field has been set. +func (o *TemplateExportResources) HasId() bool { + if o != nil && o.Id != nil { + return true + } + + return false +} + +// SetId gets a reference to the given string and assigns it to the Id field. +func (o *TemplateExportResources) SetId(v string) { + o.Id = &v +} + +// GetKind returns the Kind field value +func (o *TemplateExportResources) GetKind() TemplateKind { + if o == nil { + var ret TemplateKind + return ret + } + + return o.Kind +} + +// GetKindOk returns a tuple with the Kind field value +// and a boolean to check if the value has been set. +func (o *TemplateExportResources) GetKindOk() (*TemplateKind, bool) { + if o == nil { + return nil, false + } + return &o.Kind, true +} + +// SetKind sets field value +func (o *TemplateExportResources) SetKind(v TemplateKind) { + o.Kind = v +} + +// GetName returns the Name field value if set, zero value otherwise. +func (o *TemplateExportResources) GetName() string { + if o == nil || o.Name == nil { + var ret string + return ret + } + return *o.Name +} + +// GetNameOk returns a tuple with the Name field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TemplateExportResources) GetNameOk() (*string, bool) { + if o == nil || o.Name == nil { + return nil, false + } + return o.Name, true +} + +// HasName returns a boolean if a field has been set. +func (o *TemplateExportResources) HasName() bool { + if o != nil && o.Name != nil { + return true + } + + return false +} + +// SetName gets a reference to the given string and assigns it to the Name field. +func (o *TemplateExportResources) SetName(v string) { + o.Name = &v +} + +func (o TemplateExportResources) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if o.Id != nil { + toSerialize["id"] = o.Id + } + if true { + toSerialize["kind"] = o.Kind + } + if o.Name != nil { + toSerialize["name"] = o.Name + } + return json.Marshal(toSerialize) +} + +type NullableTemplateExportResources struct { + value *TemplateExportResources + isSet bool +} + +func (v NullableTemplateExportResources) Get() *TemplateExportResources { + return v.value +} + +func (v *NullableTemplateExportResources) Set(val *TemplateExportResources) { + v.value = val + v.isSet = true +} + +func (v NullableTemplateExportResources) IsSet() bool { + return v.isSet +} + +func (v *NullableTemplateExportResources) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableTemplateExportResources(val *TemplateExportResources) *NullableTemplateExportResources { + return &NullableTemplateExportResources{value: val, isSet: true} +} + +func (v NullableTemplateExportResources) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableTemplateExportResources) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/api/model_template_kind.gen.go b/api/model_template_kind.gen.go new file mode 100644 index 0000000..47adf98 --- /dev/null +++ b/api/model_template_kind.gen.go @@ -0,0 +1,95 @@ +/* + * 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" + "fmt" +) + +// TemplateKind the model 'TemplateKind' +type TemplateKind string + +// List of TemplateKind +const ( + TEMPLATEKIND_BUCKET TemplateKind = "Bucket" + TEMPLATEKIND_CHECK TemplateKind = "Check" + TEMPLATEKIND_CHECK_DEADMAN TemplateKind = "CheckDeadman" + TEMPLATEKIND_CHECK_THRESHOLD TemplateKind = "CheckThreshold" + TEMPLATEKIND_DASHBOARD TemplateKind = "Dashboard" + TEMPLATEKIND_LABEL TemplateKind = "Label" + TEMPLATEKIND_NOTIFICATION_ENDPOINT TemplateKind = "NotificationEndpoint" + TEMPLATEKIND_NOTIFICATION_ENDPOINT_HTTP TemplateKind = "NotificationEndpointHTTP" + TEMPLATEKIND_NOTIFICATION_ENDPOINT_PAGER_DUTY TemplateKind = "NotificationEndpointPagerDuty" + TEMPLATEKIND_NOTIFICATION_ENDPOINT_SLACK TemplateKind = "NotificationEndpointSlack" + TEMPLATEKIND_NOTIFICATION_RULE TemplateKind = "NotificationRule" + TEMPLATEKIND_TASK TemplateKind = "Task" + TEMPLATEKIND_TELEGRAF TemplateKind = "Telegraf" + TEMPLATEKIND_VARIABLE TemplateKind = "Variable" +) + +func (v *TemplateKind) UnmarshalJSON(src []byte) error { + var value string + err := json.Unmarshal(src, &value) + if err != nil { + return err + } + enumTypeValue := TemplateKind(value) + for _, existing := range []TemplateKind{"Bucket", "Check", "CheckDeadman", "CheckThreshold", "Dashboard", "Label", "NotificationEndpoint", "NotificationEndpointHTTP", "NotificationEndpointPagerDuty", "NotificationEndpointSlack", "NotificationRule", "Task", "Telegraf", "Variable"} { + if existing == enumTypeValue { + *v = enumTypeValue + return nil + } + } + + return fmt.Errorf("%+v is not a valid TemplateKind", value) +} + +// Ptr returns reference to TemplateKind value +func (v TemplateKind) Ptr() *TemplateKind { + return &v +} + +type NullableTemplateKind struct { + value *TemplateKind + isSet bool +} + +func (v NullableTemplateKind) Get() *TemplateKind { + return v.value +} + +func (v *NullableTemplateKind) Set(val *TemplateKind) { + v.value = val + v.isSet = true +} + +func (v NullableTemplateKind) IsSet() bool { + return v.isSet +} + +func (v *NullableTemplateKind) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableTemplateKind(val *TemplateKind) *NullableTemplateKind { + return &NullableTemplateKind{value: val, isSet: true} +} + +func (v NullableTemplateKind) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableTemplateKind) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/clients/export/export.go b/clients/export/export.go new file mode 100644 index 0000000..258518c --- /dev/null +++ b/clients/export/export.go @@ -0,0 +1,154 @@ +package export + +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/influxdata/influx-cli/v2/api" + "github.com/influxdata/influx-cli/v2/clients" + "gopkg.in/yaml.v3" +) + +type OutEncoding int + +const ( + YamlEncoding OutEncoding = iota + JsonEncoding +) + +type Client struct { + clients.CLI + api.TemplatesApi +} + +type Params struct { + Out io.Writer + OutEncoding + StackId string + + BucketIds []string + BucketNames []string + CheckIds []string + CheckNames []string + DashboardIds []string + DashboardNames []string + EndpointIds []string + EndpointNames []string + LabelIds []string + LabelNames []string + RuleIds []string + RuleNames []string + TaskIds []string + TaskNames []string + TelegrafIds []string + TelegrafNames []string + VariableIds []string + VariableNames []string +} + +func (c Client) Export(ctx context.Context, params *Params) error { + var exportReq api.TemplateExport + + if params.StackId != "" { + exportReq.StackID = ¶ms.StackId + } + + filters := []struct { + kind api.TemplateKind + ids []string + names []string + }{ + { + kind: api.TEMPLATEKIND_BUCKET, + ids: params.BucketIds, + names: params.BucketNames, + }, + { + kind: api.TEMPLATEKIND_CHECK, + ids: params.CheckIds, + names: params.CheckNames, + }, + { + kind: api.TEMPLATEKIND_DASHBOARD, + ids: params.DashboardIds, + names: params.DashboardNames, + }, + { + kind: api.TEMPLATEKIND_LABEL, + ids: params.LabelIds, + names: params.LabelNames, + }, + { + kind: api.TEMPLATEKIND_NOTIFICATION_ENDPOINT, + ids: params.EndpointIds, + names: params.EndpointNames, + }, + { + kind: api.TEMPLATEKIND_NOTIFICATION_RULE, + ids: params.RuleIds, + names: params.RuleNames, + }, + { + kind: api.TEMPLATEKIND_TASK, + ids: params.TaskIds, + names: params.TaskNames, + }, + { + kind: api.TEMPLATEKIND_TELEGRAF, + ids: params.TelegrafIds, + names: params.TelegrafNames, + }, + { + kind: api.TEMPLATEKIND_VARIABLE, + ids: params.VariableIds, + names: params.VariableNames, + }, + } + + for _, filter := range filters { + for _, id := range filter.ids { + id := id + exportReq.Resources = append(exportReq.Resources, api.TemplateExportResources{ + Kind: filter.kind, + Id: &id, + }) + } + for _, name := range filter.names { + name := name + exportReq.Resources = append(exportReq.Resources, api.TemplateExportResources{ + Kind: filter.kind, + Name: &name, + }) + } + } + + tmpl, err := c.ExportTemplate(ctx).TemplateExport(exportReq).Execute() + if err != nil { + return fmt.Errorf("failed to export template: %w", err) + } + if err := writeTemplate(params.Out, params.OutEncoding, tmpl); err != nil { + return fmt.Errorf("failed to write exported template: %w", err) + } + return nil +} + +func writeTemplate(out io.Writer, encoding OutEncoding, template []api.TemplateEntry) error { + switch encoding { + case JsonEncoding: + enc := json.NewEncoder(out) + enc.SetIndent("", "\t") + return enc.Encode(template) + case YamlEncoding: + enc := yaml.NewEncoder(out) + for _, entry := range template { + if err := enc.Encode(entry); err != nil { + return err + } + } + default: + return fmt.Errorf("encoding %q is not recognized", encoding) + } + return nil +} diff --git a/cmd/influx/export.go b/cmd/influx/export.go new file mode 100644 index 0000000..002843f --- /dev/null +++ b/cmd/influx/export.go @@ -0,0 +1,379 @@ +package main + +import ( + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/influxdata/influx-cli/v2/clients/export" + "github.com/influxdata/influx-cli/v2/pkg/cli/middleware" + "github.com/mattn/go-isatty" + "github.com/urfave/cli/v2" +) + +func newExportCmd() *cli.Command { + var params struct { + out string + stackId string + resourceType ResourceType + bucketIds string + bucketNames string + checkIds string + checkNames string + dashboardIds string + dashboardNames string + endpointIds string + endpointNames string + labelIds string + labelNames string + ruleIds string + ruleNames string + taskIds string + taskNames string + telegrafIds string + telegrafNames string + variableIds string + variableNames string + } + return &cli.Command{ + Name: "export", + Usage: "Export existing resources as a template", + Description: `The export command provides a mechanism to export existing resources to a +template. Each template resource kind is supported via flags. + +Examples: + # export buckets by ID + influx export --buckets=$ID1,$ID2,$ID3 + + # export buckets, labels, and dashboards by ID + influx export \ + --buckets=$BID1,$BID2,$BID3 \ + --labels=$LID1,$LID2,$LID3 \ + --dashboards=$DID1,$DID2,$DID3 + + # export all resources for a stack + influx export --stack-id $STACK_ID + + # export a stack with resources not associated with the stack + influx export --stack-id $STACK_ID --buckets $BUCKET_ID + +All of the resources are supported via the examples provided above. Provide the +resource flag and then provide the IDs. + +For information about exporting InfluxDB templates, see +https://docs.influxdata.com/influxdb/latest/reference/cli/influx/export/`, + Flags: append( + commonFlagsNoPrint(), + &cli.StringFlag{ + Name: "file", + Usage: "Output file for created template; defaults to std out if no file provided; the extension of provided file (.yml/.json) will dictate encoding", + Aliases: []string{"f"}, + Destination: ¶ms.out, + }, + &cli.StringFlag{ + Name: "stack-id", + Usage: "ID for stack to include in export", + Destination: ¶ms.stackId, + }, + &cli.GenericFlag{ + Name: "resource-type", + Usage: "If specified, strings on stdin/positional args will be treated as IDs of the given type", + Value: ¶ms.resourceType, + }, + &cli.StringFlag{ + Name: "buckets", + Usage: "List of bucket ids comma separated", + Destination: ¶ms.bucketIds, + }, + &cli.StringFlag{ + Name: "checks", + Usage: "List of check ids comma separated", + Destination: ¶ms.checkIds, + }, + &cli.StringFlag{ + Name: "dashboards", + Usage: "List of dashboard ids comma separated", + Destination: ¶ms.dashboardIds, + }, + &cli.StringFlag{ + Name: "endpoints", + Usage: "List of notification endpoint ids comma separated", + Destination: ¶ms.endpointIds, + }, + &cli.StringFlag{ + Name: "labels", + Usage: "List of label ids comma separated", + Destination: ¶ms.labelIds, + }, + &cli.StringFlag{ + Name: "rules", + Usage: "List of notification rule ids comma separated", + Destination: ¶ms.ruleIds, + }, + &cli.StringFlag{ + Name: "tasks", + Usage: "List of task ids comma separated", + Destination: ¶ms.taskIds, + }, + &cli.StringFlag{ + Name: "telegraf-configs", + Usage: "List of telegraf config ids comma separated", + Destination: ¶ms.telegrafIds, + }, + &cli.StringFlag{ + Name: "variables", + Usage: "List of variable ids comma separated", + Destination: ¶ms.variableIds, + }, + &cli.StringFlag{ + Name: "bucket-names", + Usage: "List of bucket names comma separated", + Destination: ¶ms.bucketNames, + }, + &cli.StringFlag{ + Name: "check-names", + Usage: "List of check names comma separated", + Destination: ¶ms.checkNames, + }, + &cli.StringFlag{ + Name: "dashboard-names", + Usage: "List of dashboard names comma separated", + Destination: ¶ms.dashboardNames, + }, + &cli.StringFlag{ + Name: "endpoint-names", + Usage: "List of notification endpoint names comma separated", + Destination: ¶ms.endpointNames, + }, + &cli.StringFlag{ + Name: "label-names", + Usage: "List of label names comma separated", + Destination: ¶ms.labelNames, + }, + &cli.StringFlag{ + Name: "rule-names", + Usage: "List of notification rule names comma separated", + Destination: ¶ms.ruleNames, + }, + &cli.StringFlag{ + Name: "task-names", + Usage: "List of task names comma separated", + Destination: ¶ms.taskNames, + }, + &cli.StringFlag{ + Name: "telegraf-config-names", + Usage: "List of telegraf config names comma separated", + Destination: ¶ms.telegrafNames, + }, + &cli.StringFlag{ + Name: "variable-names", + Usage: "List of variable names comma separated", + Destination: ¶ms.variableNames, + }, + ), + ArgsUsage: "[resource-id]...", + Before: middleware.WithBeforeFns(withCli(), withApi(true)), + Action: func(ctx *cli.Context) error { + parsedParams := export.Params{ + StackId: params.stackId, + BucketIds: splitNonEmpty(params.bucketIds), + BucketNames: splitNonEmpty(params.bucketNames), + CheckIds: splitNonEmpty(params.checkIds), + CheckNames: splitNonEmpty(params.checkNames), + DashboardIds: splitNonEmpty(params.dashboardIds), + DashboardNames: splitNonEmpty(params.dashboardNames), + EndpointIds: splitNonEmpty(params.endpointIds), + EndpointNames: splitNonEmpty(params.endpointNames), + LabelIds: splitNonEmpty(params.labelIds), + LabelNames: splitNonEmpty(params.labelNames), + RuleIds: splitNonEmpty(params.ruleIds), + RuleNames: splitNonEmpty(params.ruleNames), + TaskIds: splitNonEmpty(params.taskIds), + TaskNames: splitNonEmpty(params.taskNames), + TelegrafIds: splitNonEmpty(params.telegrafIds), + TelegrafNames: splitNonEmpty(params.telegrafNames), + VariableIds: splitNonEmpty(params.variableIds), + VariableNames: splitNonEmpty(params.variableNames), + } + + if params.out == "" { + parsedParams.Out = os.Stdout + } else { + f, err := os.OpenFile(params.out, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return fmt.Errorf("failed to open output path %q: %w", params.out, err) + } + defer f.Close() + parsedParams.Out = f + } + switch filepath.Ext(params.out) { + case ".json": + parsedParams.OutEncoding = export.JsonEncoding + default: // Also covers path == "" for stdout. + parsedParams.OutEncoding = export.YamlEncoding + } + + if params.resourceType != TypeUnset { + ids := ctx.Args().Slice() + + // Read any IDs from stdin. + // !IsTerminal detects when some other process is piping into this command. + if !isatty.IsTerminal(os.Stdin.Fd()) { + inBytes, err := io.ReadAll(os.Stdin) + if err != nil { + return fmt.Errorf("failed to read args from std in: %w", err) + } + ids = append(ids, strings.Fields(string(inBytes))...) + } + + switch params.resourceType { + case TypeBucket: + parsedParams.BucketIds = append(parsedParams.BucketIds, ids...) + case TypeCheck: + parsedParams.CheckIds = append(parsedParams.CheckIds, ids...) + case TypeDashboard: + parsedParams.DashboardIds = append(parsedParams.DashboardIds, ids...) + case TypeLabel: + parsedParams.LabelIds = append(parsedParams.LabelIds, ids...) + case TypeNotificationEndpoint: + parsedParams.EndpointIds = append(parsedParams.EndpointIds, ids...) + case TypeNotificationRule: + parsedParams.RuleIds = append(parsedParams.RuleIds, ids...) + case TypeTask: + parsedParams.TaskIds = append(parsedParams.TaskIds, ids...) + case TypeTelegraf: + parsedParams.TelegrafIds = append(parsedParams.TelegrafIds, ids...) + case TypeVariable: + parsedParams.VariableIds = append(parsedParams.VariableIds, ids...) + + // NOTE: The API doesn't support filtering by these resource subtypes, + // and instead converts them to the parent type. For example, + // `--resource-type notificationEndpointHTTP` gets translated to a filter + // on all notification endpoints on the server-side. I think this was + // intentional since the 2.0.x CLI didn't expose flags to filter on subtypes, + // but a bug/oversight in its parsing still allowed the subtypes through + // when passing IDs over stdin. + // Instead of allowing the type-filter to be silently converted by the server, + // we catch the previously-allowed subtypes here and return a (hopefully) useful + // error suggesting the correct flag to use. + case TypeCheckDeadman, TypeCheckThreshold: + return fmt.Errorf("filtering on resource-type %q is not supported by the API. Use resource-type %q instead", params.resourceType, TypeCheck) + case TypeNotificationEndpointHTTP, TypeNotificationEndpointPagerDuty, TypeNotificationEndpointSlack: + return fmt.Errorf("filtering on resource-type %q is not supported by the API. Use resource-type %q instead", params.resourceType, TypeNotificationEndpoint) + + default: + } + } else if ctx.NArg() > 0 { + return fmt.Errorf("must specify --resource-type when passing IDs as args") + } + + client := export.Client{ + CLI: getCLI(ctx), + TemplatesApi: getAPI(ctx).TemplatesApi, + } + return client.Export(ctx.Context, &parsedParams) + }, + } +} + +func splitNonEmpty(s string) []string { + if s == "" { + return nil + } + return strings.Split(s, ",") +} + +type ResourceType int + +const ( + TypeUnset ResourceType = iota + TypeBucket + TypeCheck + TypeCheckDeadman + TypeCheckThreshold + TypeDashboard + TypeLabel + TypeNotificationEndpoint + TypeNotificationEndpointHTTP + TypeNotificationEndpointPagerDuty + TypeNotificationEndpointSlack + TypeNotificationRule + TypeTask + TypeTelegraf + TypeVariable +) + +func (r ResourceType) String() string { + switch r { + case TypeBucket: + return "bucket" + case TypeCheck: + return "check" + case TypeCheckDeadman: + return "checkDeadman" + case TypeCheckThreshold: + return "checkThreshold" + case TypeDashboard: + return "dashboard" + case TypeLabel: + return "label" + case TypeNotificationEndpoint: + return "notificationEndpoint" + case TypeNotificationEndpointHTTP: + return "notificationEndpointHTTP" + case TypeNotificationEndpointPagerDuty: + return "notificationEndpointPagerDuty" + case TypeNotificationEndpointSlack: + return "notificationEndpointSlack" + case TypeNotificationRule: + return "notificationRule" + case TypeTask: + return "task" + case TypeTelegraf: + return "telegraf" + case TypeVariable: + return "variable" + case TypeUnset: + fallthrough + default: + return "unset" + } +} + +func (r *ResourceType) Set(v string) error { + switch strings.ToLower(v) { + case "bucket": + *r = TypeBucket + case "check": + *r = TypeCheck + case "checkdeadman": + *r = TypeCheckDeadman + case "checkthreshold": + *r = TypeCheckThreshold + case "dashboard": + *r = TypeDashboard + case "label": + *r = TypeLabel + case "notificationendpoint": + *r = TypeNotificationEndpoint + case "notificationendpointhttp": + *r = TypeNotificationEndpointHTTP + case "notificationendpointpagerduty": + *r = TypeNotificationEndpointPagerDuty + case "notificationendpointslack": + *r = TypeNotificationEndpointSlack + case "notificationrule": + *r = TypeNotificationRule + case "task": + *r = TypeTask + case "telegraf": + *r = TypeTelegraf + case "variable": + *r = TypeVariable + default: + return fmt.Errorf("unknown resource type: %s", v) + } + return nil +} diff --git a/cmd/influx/main.go b/cmd/influx/main.go index 45934d3..a907351 100644 --- a/cmd/influx/main.go +++ b/cmd/influx/main.go @@ -48,6 +48,7 @@ var app = cli.App{ newRestoreCmd(), newTelegrafsCommand(), newDashboardsCommand(), + newExportCmd(), }, } diff --git a/go.mod b/go.mod index d6e093b..b9e91e1 100644 --- a/go.mod +++ b/go.mod @@ -12,10 +12,12 @@ require ( github.com/golang/mock v1.5.0 github.com/google/go-cmp v0.5.5 github.com/kr/pretty v0.1.0 // indirect + github.com/mattn/go-isatty v0.0.13 github.com/stretchr/testify v1.7.0 github.com/urfave/cli/v2 v2.3.0 golang.org/x/text v0.3.3 golang.org/x/tools v0.1.0 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c honnef.co/go/tools v0.1.3 ) diff --git a/go.sum b/go.sum index 52ea1fb..582d966 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,9 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -71,6 +72,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=