diff --git a/openstack/rts/v1/softwareconfig/doc.go b/openstack/rts/v1/softwareconfig/doc.go new file mode 100644 index 000000000..6d1faf64f --- /dev/null +++ b/openstack/rts/v1/softwareconfig/doc.go @@ -0,0 +1,46 @@ +/* +Package softwareconfig enables management and retrieval of Software Configs + +Example to List Software Configs + + listOpts := softwareconfig.ListOpts{} + allConfigs, err := softwareconfig.List(client,listOpts) + if err != nil { + panic(err) + } + + for _, config := range allConfigs { + fmt.Printf("%+v\n", config) + } + +Example to Get Software Deployment + + configID:="bd7d48a5-6e33-4b95-aa28-d0d3af46c635" + + configs,err:=softwareconfig.Get(client,configID).Extract() + + if err != nil { + panic(err) + } + + +Example to Create a Software Configs + + createOpts := softwareconfig.CreateOpts{ + Name: "config_test", + } + + config, err := softwareconfig.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Software Configs + + configID := "8de48948-b6d6-4417-82a5-071f7811af91" + del:=softwareconfig.Delete(client,configID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package softwareconfig diff --git a/openstack/rts/v1/softwareconfig/requests.go b/openstack/rts/v1/softwareconfig/requests.go new file mode 100644 index 000000000..6f2b92a97 --- /dev/null +++ b/openstack/rts/v1/softwareconfig/requests.go @@ -0,0 +1,133 @@ +package softwareconfig + +import ( + "reflect" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the software config attributes you want to see returned. Marker and Limit are used for pagination. +type ListOpts struct { + Id string + Name string + Marker string `q:"marker"` + Limit int `q:"limit"` +} + +// List returns collection of +// Software Config. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +// +// Default policy settings return only those Software Config that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func List(c *golangsdk.ServiceClient, opts ListOpts) ([]SoftwareConfig, error) { + q, err := golangsdk.BuildQueryString(&opts) + if err != nil { + return nil, err + } + u := rootURL(c) + q.String() + pages, err := pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { + return SoftwareConfigPage{pagination.LinkedPageBase{PageResult: r}} + }).AllPages() + + allConfigs, err := ExtractSoftwareConfigs(pages) + if err != nil { + return nil, err + } + + return FilterSoftwareConfig(allConfigs, opts) +} + +func FilterSoftwareConfig(config []SoftwareConfig, opts ListOpts) ([]SoftwareConfig, error) { + + var refinedSoftwareConfig []SoftwareConfig + var matched bool + m := map[string]interface{}{} + + if opts.Id != "" { + m["Id"] = opts.Id + } + if opts.Name != "" { + m["Name"] = opts.Name + } + + if len(m) > 0 && len(config) > 0 { + for _, config := range config { + matched = true + + for key, value := range m { + if sVal := getStructField(&config, key); !(sVal == value) { + matched = false + } + } + + if matched { + refinedSoftwareConfig = append(refinedSoftwareConfig, config) + } + } + } else { + refinedSoftwareConfig = config + } + return refinedSoftwareConfig, nil +} + +func getStructField(v *SoftwareConfig, field string) string { + r := reflect.ValueOf(v) + f := reflect.Indirect(r).FieldByName(field) + return string(f.String()) +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToSoftwareConfigCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains all the values needed to create a new Software Config. There are +// no required values. +type CreateOpts struct { + // Specifies the script used for defining the configuration. + Config string `json:"config,omitempty"` + //Specifies the name of the software configuration group. + Group string `json:"group,omitempty"` + //Specifies the name of the software configuration. + Name string `json:"name" required:"true"` + //Specifies the software configuration input. + Inputs []map[string]interface{} `json:"inputs,omitempty"` + //Specifies the software configuration output. + Outputs []map[string]interface{} `json:"outputs,omitempty"` + //Specifies options used by a software configuration management tool. + Options map[string]interface{} `json:"options,omitempty"` +} + +// ToSoftwareConfigCreateMap builds a create request body from CreateOpts. +func (opts CreateOpts) ToSoftwareConfigCreateMap() (map[string]interface{}, error) { + return golangsdk.BuildRequestBody(opts, "") +} + +// Create accepts a CreateOpts struct and uses the values to create a new Software config +func Create(c *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToSoftwareConfigCreateMap() + if err != nil { + r.Err = err + return + } + reqOpt := &golangsdk.RequestOpts{OkCodes: []int{200}} + _, r.Err = c.Post(rootURL(c), b, &r.Body, reqOpt) + return +} + +// Get retrieves a particular software config based on its unique ID. +func Get(c *golangsdk.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} + +// Delete will permanently delete a particular Software Config based on its unique ID. +func Delete(c *golangsdk.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/rts/v1/softwareconfig/results.go b/openstack/rts/v1/softwareconfig/results.go new file mode 100644 index 000000000..4ba0d5a36 --- /dev/null +++ b/openstack/rts/v1/softwareconfig/results.go @@ -0,0 +1,99 @@ +package softwareconfig + +import ( + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +type SoftwareConfig struct { + // Specifies the software configuration input. + Inputs []map[string]interface{} `json:"inputs"` + //Specifies the name of the software configuration. + Name string `json:"name"` + //Specifies the software configuration output. + Outputs []map[string]interface{} `json:"outputs"` + //Specifies the time when a configuration is created. + CreationTime golangsdk.JSONRFC3339NoZ `json:"creation_time"` + //Specifies the name of the software configuration group. + Group string `json:"group"` + //Specifies the configuration code. + Config string `json:"config"` + //Specifies configuration options. + Options map[string]interface{} `json:"options"` + //Specifies the software configuration ID. + Id string `json:"id"` +} + +// SoftwareConfigPage is the page returned by a pager when traversing over a +// collection of Software Configurations. +type SoftwareConfigPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of Software Configs has reached +// the end of a page and the pager seeks to traverse over a new one. In order +// to do this, it needs to construct the next page's URL. +func (r SoftwareConfigPage) NextPageURL() (string, error) { + var s struct { + Links []golangsdk.Link `json:"software_config_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return golangsdk.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a SoftwareConfigPage struct is empty. +func (r SoftwareConfigPage) IsEmpty() (bool, error) { + is, err := ExtractSoftwareConfigs(r) + return len(is) == 0, err +} + +// ExtractSoftwareConfigs accepts a Page struct, specifically a SoftwareConfigPage struct, +// and extracts the elements into a slice of Software Configs structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractSoftwareConfigs(r pagination.Page) ([]SoftwareConfig, error) { + var s struct { + SoftwareConfigs []SoftwareConfig `json:"software_configs"` + } + err := (r.(SoftwareConfigPage)).ExtractInto(&s) + return s.SoftwareConfigs, err +} + +type commonResult struct { + golangsdk.Result +} + +// Extract is a function that accepts a result and extracts a Software configuration. +func (r commonResult) Extract() (*SoftwareConfig, error) { + var s struct { + SoftwareConfig *SoftwareConfig `json:"software_config"` + } + err := r.ExtractInto(&s) + return s.SoftwareConfig, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Software configuration. +type CreateResult struct { + commonResult +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Software configuration. +type GetResult struct { + commonResult +} + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Software configuration. +type UpdateResult struct { + commonResult +} + +// DeleteResult represents the result of a delete operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type DeleteResult struct { + golangsdk.ErrResult +} diff --git a/openstack/rts/v1/softwareconfig/testing/fixtures.go b/openstack/rts/v1/softwareconfig/testing/fixtures.go new file mode 100644 index 000000000..37caee341 --- /dev/null +++ b/openstack/rts/v1/softwareconfig/testing/fixtures.go @@ -0,0 +1,185 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/huaweicloud/golangsdk/openstack/rts/v1/softwareconfig" + th "github.com/huaweicloud/golangsdk/testhelper" + fake "github.com/huaweicloud/golangsdk/testhelper/client" +) + +// CreateExpected represents the expected object from a Create request. +var CreateExpected = &softwareconfig.SoftwareConfig{ + Inputs: []map[string]interface{}{{"type": "String", "name": "foo"}, + {"type": "String", "name": "bar"}}, + Group: "script", + Name: "test-cong", + Outputs: []map[string]interface{}{{"type": "String", "name": "result", "error_output": "false"}}, + Config: "#!/bin/sh -x\necho \"Writing to /tmp/$bar\"\necho $foo > /tmp/$bar\necho -n \"The file /tmp/$bar contains cat /tmp/$bar for server $deploy_server_id during $deploy_action\" > $heat_outputs_path.result\necho \"Written to /tmp/$bar\"\necho \"Output to stderr\" 1>&2", + Id: "e0be7e37-a581-4b24-bfb1-df4f3048c090", +} + +// CreateOutput represents the response body from a Create request. +const CreateOutput = ` +{ + "software_config": { + "inputs": [ + { + "type": "String", + "name": "foo" + }, + { + "type": "String", + "name": "bar" + } + ], + "group": "script", + "name": "test-cong", + "outputs": [ + { + "type": "String", + "name": "result", + "error_output": "false" + } + ], + "config": "#!/bin/sh -x\necho \"Writing to /tmp/$bar\"\necho $foo > /tmp/$bar\necho -n \"The file /tmp/$bar contains cat /tmp/$bar for server $deploy_server_id during $deploy_action\" > $heat_outputs_path.result\necho \"Written to /tmp/$bar\"\necho \"Output to stderr\" 1>&2", + "id": "e0be7e37-a581-4b24-bfb1-df4f3048c090" + } +}` + +// HandleCreateSuccessfully creates an HTTP handler at `/stacks` on the test handler mux +// that responds with a `Create` response. +func HandleCreateSuccessfully(t *testing.T, output string) { + th.Mux.HandleFunc("/software_configs", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, output) + }) +} + +// ListExpected represents the expected object from a List request. +var ListExpected = []softwareconfig.SoftwareConfig{ + { + Group: "script", + Name: "test-cong", + Id: "e0be7e37-a581-4b24-bfb1-df4f3048c090", + }, + { + Group: "script", + Name: "test-cong1", + Id: "743a15f6-9a55-49fe-80bb-a9188f39fc07", + }, + { + Group: "script", + Name: "a-config-we5zpvyu7b5o", + Id: "a6ff3598-f2e0-4111-81b0-aa3e1cac2529", + }, +} + +// FullListOutput represents the response body from a List request without a marker. +const FullListOutput = ` +{ + "software_configs": [ + { + "group": "script", + "id": "e0be7e37-a581-4b24-bfb1-df4f3048c090", + "name": "test-cong" + }, + { + "group": "script", + "id": "743a15f6-9a55-49fe-80bb-a9188f39fc07", + "name": "test-cong1" + }, + { + "group": "script", + "id": "a6ff3598-f2e0-4111-81b0-aa3e1cac2529", + "name": "a-config-we5zpvyu7b5o" + } + ] +}` + +// HandleListSuccessfully creates an HTTP handler at `/stacks` on the test handler mux +// that responds with a `List` response. +func HandleListSuccessfully(t *testing.T, output string) { + th.Mux.HandleFunc("/software_configs", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, output) + //r.ParseForm() + }) +} + +// GetExpected represents the expected object from a Get request. +var GetExpected = &softwareconfig.SoftwareConfig{ + Inputs: []map[string]interface{}{{"type": "String", "name": "foo"}, + {"type": "String", "name": "bar"}}, + Group: "script", + Name: "test-cong", + Outputs: []map[string]interface{}{{"type": "String", "name": "result", "error_output": "false"}}, + Config: "#!/bin/sh -x\necho \"Writing to /tmp/$bar\"\necho $foo > /tmp/$bar\necho -n \"The file /tmp/$bar contains cat /tmp/$bar for server $deploy_server_id during $deploy_action\" > $heat_outputs_path.result\necho \"Written to /tmp/$bar\"\necho \"Output to stderr\" 1>&2", + Id: "e0be7e37-a581-4b24-bfb1-df4f3048c090", +} + +// GetOutput represents the response body from a Get request. +const GetOutput = ` +{ + "software_config": { + "inputs": [ + { + "type": "String", + "name": "foo" + }, + { + "type": "String", + "name": "bar" + } + ], + "group": "script", + "name": "test-cong", + "outputs": [ + { + "type": "String", + "name": "result", + "error_output": "false" + } + ], + "config": "#!/bin/sh -x\necho \"Writing to /tmp/$bar\"\necho $foo > /tmp/$bar\necho -n \"The file /tmp/$bar contains cat /tmp/$bar for server $deploy_server_id during $deploy_action\" > $heat_outputs_path.result\necho \"Written to /tmp/$bar\"\necho \"Output to stderr\" 1>&2", + "id": "e0be7e37-a581-4b24-bfb1-df4f3048c090" + } +}` + +// HandleGetSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` +// on the test handler mux that responds with a `Get` response. +func HandleGetSuccessfully(t *testing.T, output string) { + th.Mux.HandleFunc("/software_configs/e0be7e37-a581-4b24-bfb1-df4f3048c090", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, output) + }) +} + +// HandleDeleteSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` +// on the test handler mux that responds with a `Delete` response. +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/software_configs/e2fe5553-a481-4549-9d0f-e208de3d98d1", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/rts/v1/softwareconfig/testing/requests_test.go b/openstack/rts/v1/softwareconfig/testing/requests_test.go new file mode 100644 index 000000000..b4e935403 --- /dev/null +++ b/openstack/rts/v1/softwareconfig/testing/requests_test.go @@ -0,0 +1,56 @@ +package testing + +import ( + "testing" + + "github.com/huaweicloud/golangsdk/openstack/rts/v1/softwareconfig" + th "github.com/huaweicloud/golangsdk/testhelper" + fake "github.com/huaweicloud/golangsdk/testhelper/client" +) + +func TestCreateSoftwareConfig(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSuccessfully(t, CreateOutput) + createOpts := softwareconfig.CreateOpts{Name: "test-cong"} + actual, err := softwareconfig.Create(fake.ServiceClient(), createOpts).Extract() + + th.AssertNoErr(t, err) + + expected := CreateExpected + th.AssertDeepEquals(t, expected, actual) +} + +func TestListSoftwareConfig(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t, FullListOutput) + + actual, err := softwareconfig.List(fake.ServiceClient(), softwareconfig.ListOpts{}) + if err != nil { + t.Errorf("Failed to extract vpcs: %v", err) + } + th.AssertDeepEquals(t, ListExpected, actual) + th.AssertNoErr(t, err) +} + +func TestGetSoftwareConfig(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t, GetOutput) + + actual, err := softwareconfig.Get(fake.ServiceClient(), "e0be7e37-a581-4b24-bfb1-df4f3048c090").Extract() + th.AssertNoErr(t, err) + + expected := GetExpected + th.AssertDeepEquals(t, expected, actual) +} + +func TestDeleteSoftwareConfig(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfully(t) + + err := softwareconfig.Delete(fake.ServiceClient(), "e2fe5553-a481-4549-9d0f-e208de3d98d1").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/rts/v1/softwareconfig/urls.go b/openstack/rts/v1/softwareconfig/urls.go new file mode 100644 index 000000000..e9161e3e5 --- /dev/null +++ b/openstack/rts/v1/softwareconfig/urls.go @@ -0,0 +1,13 @@ +package softwareconfig + +import "github.com/huaweicloud/golangsdk" + +const resourcePath = "software_configs" + +func rootURL(c *golangsdk.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func resourceURL(c *golangsdk.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id) +} diff --git a/openstack/rts/v1/softwaredeployment/doc.go b/openstack/rts/v1/softwaredeployment/doc.go new file mode 100644 index 000000000..d1d35ec44 --- /dev/null +++ b/openstack/rts/v1/softwaredeployment/doc.go @@ -0,0 +1,70 @@ +/* +Package softwaredeployment enables management and retrieval of Software Deployments + +Example to List Software Deployments + + listOpts := softwaredeployment.ListOpts{} + allDeployments, err := softwaredeployment.List(client,listOpts) + if err != nil { + panic(err) + } + + for _, deployment := range allDeployments { + fmt.Printf("%+v\n", allDeployments) + } + +Example to Get Software Deployment + + deploymentID:="bd7d48a5-6e33-4b95-aa28-d0d3af46c635" + + deployments,err:=softwaredeployment.Get(client,deploymentID).Extract() + + if err != nil { + panic(err) + } + +Example to Create a Software Deployments + + input:=map[string]interface{}{"name":"foo"} + + createOpts := softwaredeployment.CreateOpts{ + Status:"IN_PROGRESS", + ServerId:"f274ac7d-334d-41ff-83bd-1de669f7781b", + ConfigId:"a6ff3598-f2e0-4111-81b0-aa3e1cac2529", + InputValues:input, + TenantId:"17fbda95add24720a4038ba4b1c705ed", + Action:"CREATE" + } + + deployment, err := softwaredeployment.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Software Deployments + deploymentID:="bd7d48a5-6e33-4b95-aa28-d0d3af46c635" + + ouput:=map[string]interface{}{"deploy_stdout":"Writing to /tmp/baaaaa\nWritten to /tmp/baaaaa\n","deploy_stderr":"+ echo Writing to /tmp/baaaaa\n+ echo fooooo\n+ cat /tmp/baaaaa\n+ echo -n The file /tmp/baaaaa contains fooooo for server ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5 during CREATE\n+ echo Written to /tmp/baaaaa\n+ echo Output to stderr\nOutput to stderr\n", + "deploy_status_code":0,"result":"The file /tmp/baaaaa contains fooooo for server ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5 during CREATE"} + + updateOpts := softwaredeployment.UpdateOpts{ + Status:"COMPLETE", + ConfigId:"a6ff3598-f2e0-4111-81b0-aa3e1cac2529", + OutputValues:ouput, + StatusReason:"Outputs received"} + + deployment, err := softwaredeployment.Update(client, deploymentID, updateOpts).Extract() + if err != nil { + panic(err) + } + + +Example to Delete a Software Deployments + + deploymentID:="bd7d48a5-6e33-4b95-aa28-d0d3af46c635" + del:=softwaredeployment.Delete(client,deploymentID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package softwaredeployment diff --git a/openstack/rts/v1/softwaredeployment/requests.go b/openstack/rts/v1/softwaredeployment/requests.go new file mode 100644 index 000000000..c0da7ef9f --- /dev/null +++ b/openstack/rts/v1/softwaredeployment/requests.go @@ -0,0 +1,190 @@ +package softwaredeployment + +import ( + "reflect" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. +type ListOpts struct { + //Specifies the ID of the instance deployed by the software configuration. + ServerId string `q:"server_id"` + //Specifies the ID of this deployment resource. + Id string + //Specifies the ID of the software configuration resource running on an instance. + ConfigId string + //Specifies the current status of deployment resources. Valid values include COMPLETE, IN_PROGRESS, and FAILED. + Status string + // Specifies the stack action that triggers this deployment resource. + Action string +} + +// List returns collection of +// Software Deployment. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +// +// Default policy settings return only those Software Deployment that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func List(client *golangsdk.ServiceClient, opts ListOpts) ([]Deployment, error) { + q, err := golangsdk.BuildQueryString(&opts) + if err != nil { + return nil, err + } + u := rootURL(client) + q.String() + pages, err := pagination.NewPager(client, u, func(r pagination.PageResult) pagination.Page { + return DeploymentPage{pagination.LinkedPageBase{PageResult: r}} + }).AllPages() + + allConfigs, err := ExtractDeployments(pages) + if err != nil { + return nil, err + } + + return FilterDeployments(allConfigs, opts) +} + +func FilterDeployments(deployments []Deployment, opts ListOpts) ([]Deployment, error) { + + var refinedDeployments []Deployment + var matched bool + m := map[string]interface{}{} + + if opts.Id != "" { + m["Id"] = opts.Id + } + if opts.ServerId != "" { + m["ServerId"] = opts.ServerId + } + if opts.ConfigId != "" { + m["ConfigId"] = opts.ConfigId + } + if opts.Status != "" { + m["Status"] = opts.Status + } + if opts.Action != "" { + m["Action"] = opts.Action + } + + if len(m) > 0 && len(deployments) > 0 { + for _, deployment := range deployments { + matched = true + + for key, value := range m { + if sVal := getStructField(&deployment, key); !(sVal == value) { + matched = false + } + } + + if matched { + refinedDeployments = append(refinedDeployments, deployment) + } + } + + } else { + refinedDeployments = deployments + } + + return refinedDeployments, nil +} + +func getStructField(v *Deployment, field string) string { + r := reflect.ValueOf(v) + f := reflect.Indirect(r).FieldByName(field) + return string(f.String()) +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToSoftwareDeploymentCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains all the values needed to create a new Software Deployment. There are +// no required values. +type CreateOpts struct { + // Specifies the stack action that triggers this deployment resource. + Action string `json:"action,omitempty"` + //Specifies the ID of the software configuration resource running on an instance. + ConfigId string `json:"config_id" required:"true"` + //Specifies input data stored in the form of a key-value pair. + InputValues map[string]interface{} `json:"input_values,omitempty"` + //Specifies the ID of the instance deployed by the software configuration. + ServerId string `json:"server_id" required:"true"` + //Specifies the ID of the authenticated tenant who can perform operations on the deployment resources. + TenantId string `json:"stack_user_project_id,omitempty"` + //Specifies the current status of deployment resources. Valid values include COMPLETE, IN_PROGRESS, and FAILED. + Status string `json:"status,omitempty"` + //Specifies the cause of the current deployment resource status. + StatusReason string `json:"status_reason,omitempty"` +} + +// ToSoftwareDeploymentCreateMap builds a create request body from CreateOpts. +func (opts CreateOpts) ToSoftwareDeploymentCreateMap() (map[string]interface{}, error) { + return golangsdk.BuildRequestBody(opts, "") +} + +// Create accepts a CreateOpts struct and uses the values to create a new Software Deployment +func Create(c *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToSoftwareDeploymentCreateMap() + if err != nil { + r.Err = err + return + } + reqOpt := &golangsdk.RequestOpts{OkCodes: []int{200}} + _, r.Err = c.Post(rootURL(c), b, &r.Body, reqOpt) + return +} + +// Get retrieves a particular software Deployment based on its unique ID. +func Get(c *golangsdk.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} + +//UpdateOptsBuilder is an interface by which can be able to build the request body of software deployment. +type UpdateOptsBuilder interface { + ToSoftwareDeploymentUpdateMap() (map[string]interface{}, error) +} + +//UpdateOpts is a struct which represents the request body of update method. +type UpdateOpts struct { + // Specifies the stack action that triggers this deployment resource. + Action string `json:"action,omitempty"` + //Specifies the ID of the software configuration resource running on an instance. + ConfigId string `json:"config_id" required:"true"` + //Specifies input data stored in the form of a key-value pair. + InputValues map[string]interface{} `json:"input_values,omitempty"` + //Specifies output data stored in the form of a key-value pair. + OutputValues map[string]interface{} `json:"output_values" required:"true"` + //Specifies the current status of deployment resources. Valid values include COMPLETE, IN_PROGRESS, and FAILED. + Status string `json:"status,omitempty"` + //Specifies the cause of the current deployment resource status. + StatusReason string `json:"status_reason,omitempty"` +} + +//ToSoftwareDeploymentUpdateMap builds a update request body from UpdateOpts. +func (opts UpdateOpts) ToSoftwareDeploymentUpdateMap() (map[string]interface{}, error) { + return golangsdk.BuildRequestBody(opts, "") +} + +//Update is a method which can be able to update the name of software deployment. +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToSoftwareDeploymentUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(resourceURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Delete will permanently delete a particular Software Deployment based on its unique ID. +func Delete(c *golangsdk.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/rts/v1/softwaredeployment/results.go b/openstack/rts/v1/softwaredeployment/results.go new file mode 100644 index 000000000..74d37830d --- /dev/null +++ b/openstack/rts/v1/softwaredeployment/results.go @@ -0,0 +1,103 @@ +package softwaredeployment + +import ( + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +type Deployment struct { + // Specifies the stack action that triggers this deployment resource. + Action string `json:"action"` + //Specifies the ID of the software Deployments resource running on an instance. + ConfigId string `json:"config_id"` + //Specifies the creation time. The timestamp format is ISO 8601: CCYY-MM-DDThh:mm:ss±hh:mm + CreationTime golangsdk.JSONRFC3339NoZ `json:"creation_time"` + //Specifies the ID of this deployment resource. + Id string `json:"id"` + //Specifies input data stored in the form of a key-value pair. + InputValues map[string]interface{} `json:"input_values"` + //Specifies output data stored in the form of a key-value pair. + OutputValues map[string]interface{} `json:"output_values"` + //Specifies the ID of the instance deployed by the software Deployments. + ServerId string `json:"server_id"` + //Specifies the current status of deployment resources. Valid values include COMPLETE, IN_PROGRESS, and FAILED. + Status string `json:"status"` + //Specifies the cause of the current deployment resource status. + StatusReason string `json:"status_reason"` + //Specifies the updated time. The timestamp format is ISO 8601: CCYY-MM-DDThh:mm:ss±hh:mm + UpdatedTime golangsdk.JSONRFC3339NoZ `json:"updated_time"` +} + +// DeploymentPage is the page returned by a pager when traversing over a +// collection of Software Deployments. +type DeploymentPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of Software Deployments has reached +// the end of a page and the pager seeks to traverse over a new one. In order +// to do this, it needs to construct the next page's URL. +func (r DeploymentPage) NextPageURL() (string, error) { + var s struct { + Links []golangsdk.Link `json:"software_deployments_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return golangsdk.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a DeploymentPage struct is empty. +func (r DeploymentPage) IsEmpty() (bool, error) { + is, err := ExtractDeployments(r) + return len(is) == 0, err +} + +// ExtractDeployments accepts a Page struct, specifically a DeploymentPage struct, +// and extracts the elements into a slice of Software Deployments structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractDeployments(r pagination.Page) ([]Deployment, error) { + var s struct { + Deployments []Deployment `json:"software_deployments"` + } + err := (r.(DeploymentPage)).ExtractInto(&s) + return s.Deployments, err +} + +type commonResult struct { + golangsdk.Result +} + +// Extract is a function that accepts a result and extracts a Software Deployments. +func (r commonResult) Extract() (*Deployment, error) { + var s struct { + SoftwareDeployment *Deployment `json:"software_deployment"` + } + err := r.ExtractInto(&s) + return s.SoftwareDeployment, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Software Deployments. +type CreateResult struct { + commonResult +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Software Deployments. +type GetResult struct { + commonResult +} + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Software Deployments. +type UpdateResult struct { + commonResult +} + +// DeleteResult represents the result of a delete operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type DeleteResult struct { + golangsdk.ErrResult +} diff --git a/openstack/rts/v1/softwaredeployment/testing/fixtures.go b/openstack/rts/v1/softwaredeployment/testing/fixtures.go new file mode 100644 index 000000000..963f875ff --- /dev/null +++ b/openstack/rts/v1/softwaredeployment/testing/fixtures.go @@ -0,0 +1,198 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/huaweicloud/golangsdk/openstack/rts/v1/softwaredeployment" + th "github.com/huaweicloud/golangsdk/testhelper" + fake "github.com/huaweicloud/golangsdk/testhelper/client" +) + +// CreateExpected represents the expected object from a Create request. +var CreateExpected = &softwaredeployment.Deployment{ + Id: "43489279-7b12-4fc5-90ed-320f29e89419", + ConfigId: "69070672-d37d-4095-a19c-52ab1fde9a24", + ServerId: "a161a111-03a0-4204-b5c9-5df46587df5e", + Status: "IN_PROGRESS", + Action: "CREATE", + StatusReason: "Deploy data available", +} + +// CreateOutput represents the response body from a Create request. +const CreateOutput = ` +{ + "software_deployment": { + "status": "IN_PROGRESS", + "server_id": "a161a111-03a0-4204-b5c9-5df46587df5e", + "config_id": "69070672-d37d-4095-a19c-52ab1fde9a24", + "action": "CREATE", + "status_reason": "Deploy data available", + "id": "43489279-7b12-4fc5-90ed-320f29e89419" + } +}` + +// HandleCreateSuccessfully creates an HTTP handler at `/stacks` on the test handler mux +// that responds with a `Create` response. +func HandleCreateSuccessfully(t *testing.T, output string) { + th.Mux.HandleFunc("/software_deployments", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, output) + }) +} + +// CreateExpected represents the expected object from a Create request. +var ListExpected = []softwaredeployment.Deployment{ + { + Id: "b357fbe9-4af8-4d00-9499-a13f0d0150bb", + ConfigId: "a6ff3598-f2e0-4111-81b0-aa3e1cac2529", + ServerId: "e4b191b0-b80b-4782-994c-02abb094480e", + Status: "IN_PROGRESS", + Action: "CREATE", + StatusReason: "Deploy data available", + }, + { + Id: "43489279-7b12-4fc5-90ed-320f29e89419", + ConfigId: "69070672-d37d-4095-a19c-52ab1fde9a24", + ServerId: "a161a111-03a0-4204-b5c9-5df46587df5e", + Status: "IN_PROGRESS", + Action: "CREATE", + StatusReason: "Deploy data available", + }, +} + +// CreateOutput represents the response body from a Create request. +const ListOutput = ` +{ + "software_deployments": [ + { + "status": "IN_PROGRESS", + "server_id": "e4b191b0-b80b-4782-994c-02abb094480e", + "config_id": "a6ff3598-f2e0-4111-81b0-aa3e1cac2529", + "action": "CREATE", + "status_reason": "Deploy data available", + "id": "b357fbe9-4af8-4d00-9499-a13f0d0150bb" + }, + { + "status": "IN_PROGRESS", + "server_id": "a161a111-03a0-4204-b5c9-5df46587df5e", + "config_id": "69070672-d37d-4095-a19c-52ab1fde9a24", + "action": "CREATE", + "status_reason": "Deploy data available", + "id": "43489279-7b12-4fc5-90ed-320f29e89419" + } + ] +}` + +func HandleListSuccessfully(t *testing.T, output string) { + th.Mux.HandleFunc("/software_deployments", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, output) + //r.ParseForm() + }) +} + +// GetExpected represents the expected object from a Get request. +var GetExpected = &softwaredeployment.Deployment{ + Id: "43489279-7b12-4fc5-90ed-320f29e89419", + ConfigId: "69070672-d37d-4095-a19c-52ab1fde9a24", + ServerId: "a161a111-03a0-4204-b5c9-5df46587df5e", + Status: "IN_PROGRESS", + Action: "CREATE", + StatusReason: "Deploy data available", +} + +// GetOutput represents the response body from a Get request. +const GetOutput = ` +{ + "software_deployment": { + "status": "IN_PROGRESS", + "server_id": "a161a111-03a0-4204-b5c9-5df46587df5e", + "config_id": "69070672-d37d-4095-a19c-52ab1fde9a24", + "action": "CREATE", + "status_reason": "Deploy data available", + "id": "43489279-7b12-4fc5-90ed-320f29e89419" + } +}` + +// HandleGetSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` +// on the test handler mux that responds with a `Get` response. +func HandleGetSuccessfully(t *testing.T, output string) { + th.Mux.HandleFunc("/software_deployments/43489279-7b12-4fc5-90ed-320f29e89419", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, output) + }) +} + +// GetExpected represents the expected object from a Get request. +var UpdateExpected = &softwaredeployment.Deployment{ + Id: "43489279-7b12-4fc5-90ed-320f29e89419", + ConfigId: "69070672-d37d-4095-a19c-52ab1fde9a24", + ServerId: "a161a111-03a0-4204-b5c9-5df46587df5e", + Status: "COMPLETE", + Action: "CREATE", + StatusReason: "Outputs received", + OutputValues: map[string]interface{}{"deploy_stdout": "Writing to /tmp/baaaaa\nWritten to /tmp/baaaaa\n", "deploy_stderr": "+ echo Writing to /tmp/baaaaa\n+ echo fooooo\n+ cat /tmp/baaaaa\n+ echo -n The file /tmp/baaaaa contains fooooo for server ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5 during CREATE\n+ echo Written to /tmp/baaaaa\n+ echo Output to stderr\nOutput to stderr\n", + "deploy_status_code": "0", "result": "The file /tmp/baaaaa contains fooooo for server ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5 during CREATE"}, +} + +// GetOutput represents the response body from a Get request. +const UpdateOutput = ` +{ + "software_deployment": { + "status": "COMPLETE", + "server_id": "a161a111-03a0-4204-b5c9-5df46587df5e", + "config_id": "69070672-d37d-4095-a19c-52ab1fde9a24", + "output_values": { + "deploy_stdout": "Writing to /tmp/baaaaa\nWritten to /tmp/baaaaa\n", + "deploy_stderr": "+ echo Writing to /tmp/baaaaa\n+ echo fooooo\n+ cat /tmp/baaaaa\n+ echo -n The file /tmp/baaaaa contains fooooo for server ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5 during CREATE\n+ echo Written to /tmp/baaaaa\n+ echo Output to stderr\nOutput to stderr\n", + "deploy_status_code": "0", + "result": "The file /tmp/baaaaa contains fooooo for server ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5 during CREATE" + }, + "action": "CREATE", + "status_reason": "Outputs received", + "id": "43489279-7b12-4fc5-90ed-320f29e89419" + } +}` + +// HandleUpdateSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` +// on the test handler mux that responds with a `Get` response. +func HandleUpdateSuccessfully(t *testing.T, output string) { + th.Mux.HandleFunc("/software_deployments/43489279-7b12-4fc5-90ed-320f29e89419", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, output) + }) +} + +// HandleDeleteSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` +// on the test handler mux that responds with a `Delete` response. +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/software_deployments/43489279-7b12-4fc5-90ed-320f29e89419", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/rts/v1/softwaredeployment/testing/requests_test.go b/openstack/rts/v1/softwaredeployment/testing/requests_test.go new file mode 100644 index 000000000..6624db650 --- /dev/null +++ b/openstack/rts/v1/softwaredeployment/testing/requests_test.go @@ -0,0 +1,82 @@ +package testing + +import ( + "testing" + + "github.com/huaweicloud/golangsdk/openstack/rts/v1/softwaredeployment" + th "github.com/huaweicloud/golangsdk/testhelper" + fake "github.com/huaweicloud/golangsdk/testhelper/client" +) + +func TestCreateSoftwareDeployment(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSuccessfully(t, CreateOutput) + + createOpts := softwaredeployment.CreateOpts{ + ConfigId: "031e3891-d183-4f8e-a836-589a5dce541c", + ServerId: "b7653627-9b2a-4b61-b18f-e20ea88ee924", + TenantId: "17fbda95add24720a4038ba4b1c705ed", + Status: "IN_PROGRESS", + Action: "CREATE", + StatusReason: "Deploy data available", + } + + actual, err := softwaredeployment.Create(fake.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + expected := CreateExpected + th.CheckDeepEquals(t, expected, actual) +} + +func TestListSoftwareDeployment(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t, ListOutput) + + listOpts := softwaredeployment.ListOpts{} + actual, err := softwaredeployment.List(fake.ServiceClient(), listOpts) + + th.AssertNoErr(t, err) + expected := ListExpected + th.CheckDeepEquals(t, expected, actual) +} + +func TestGetSoftwareDeployment(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t, GetOutput) + actual, err := softwaredeployment.Get(fake.ServiceClient(), "43489279-7b12-4fc5-90ed-320f29e89419").Extract() + + th.AssertNoErr(t, err) + expected := GetExpected + th.CheckDeepEquals(t, expected, actual) +} + +func TestUpdateSoftwareDeployment(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateSuccessfully(t, UpdateOutput) + ouput := map[string]interface{}{ + "deploy_stdout": "Writing to /tmp/baaaaa\nWritten to /tmp/baaaaa\n", + "deploy_stderr": "+ echo Writing to /tmp/baaaaa\n+ echo fooooo\n+ cat /tmp/baaaaa\n+ echo -n The file /tmp/baaaaa contains fooooo for server ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5 during CREATE\n+ echo Written to /tmp/baaaaa\n+ echo Output to stderr\nOutput to stderr\n", + "deploy_status_code": "0", "result": "The file /tmp/baaaaa contains fooooo for server ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5 during CREATE"} + updateOpts := softwaredeployment.UpdateOpts{ + Status: "COMPLETE", + ConfigId: "a6ff3598-f2e0-4111-81b0-aa3e1cac2529", + OutputValues: ouput, + StatusReason: "Outputs received"} + actual, err := softwaredeployment.Update(fake.ServiceClient(), "43489279-7b12-4fc5-90ed-320f29e89419", updateOpts).Extract() + + th.AssertNoErr(t, err) + expected := UpdateExpected + th.CheckDeepEquals(t, expected, actual) +} + +func TestDeleteSoftwareConfig(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfully(t) + + err := softwaredeployment.Delete(fake.ServiceClient(), "43489279-7b12-4fc5-90ed-320f29e89419").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/rts/v1/softwaredeployment/urls.go b/openstack/rts/v1/softwaredeployment/urls.go new file mode 100644 index 000000000..bf92de4ba --- /dev/null +++ b/openstack/rts/v1/softwaredeployment/urls.go @@ -0,0 +1,13 @@ +package softwaredeployment + +import "github.com/huaweicloud/golangsdk" + +const resourcePath = "software_deployments" + +func rootURL(c *golangsdk.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func resourceURL(c *golangsdk.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id) +}