diff --git a/client/v3/v3_service.go b/client/v3/v3_service.go index 3e7222c9a..69ec1b081 100644 --- a/client/v3/v3_service.go +++ b/client/v3/v3_service.go @@ -72,11 +72,41 @@ type Service interface { UpdateProject(uuid string, body *Project) (*Project, error) DeleteProject(uuid string) error CreateAccessControlPolicy(request *AccessControlPolicy) (*AccessControlPolicy, error) - GetAccessControlPolicy(projectUUID string) (*AccessControlPolicy, error) + GetAccessControlPolicy(accessControlPolicyUUID string) (*AccessControlPolicy, error) ListAccessControlPolicy(getEntitiesRequest *DSMetadata) (*AccessControlPolicyListResponse, error) ListAllAccessControlPolicy(filter string) (*AccessControlPolicyListResponse, error) UpdateAccessControlPolicy(uuid string, body *AccessControlPolicy) (*AccessControlPolicy, error) DeleteAccessControlPolicy(uuid string) (*DeleteResponse, error) + CreateRole(request *Role) (*Role, error) + GetRole(uuid string) (*Role, error) + ListRole(getEntitiesRequest *DSMetadata) (*RoleListResponse, error) + ListAllRole(filter string) (*RoleListResponse, error) + UpdateRole(uuid string, body *Role) (*Role, error) + DeleteRole(uuid string) (*DeleteResponse, error) + CreateUser(request *UserIntentInput) (*UserIntentResponse, error) + GetUser(userUUID string) (*UserIntentResponse, error) + UpdateUser(uuid string, body *UserIntentInput) (*UserIntentResponse, error) + DeleteUser(uuid string) (*DeleteResponse, error) + ListUser(getEntitiesRequest *DSMetadata) (*UserListResponse, error) + ListAllUser(filter string) (*UserListResponse, error) + GetUserGroup(userUUID string) (*UserGroupIntentResponse, error) + ListUserGroup(getEntitiesRequest *DSMetadata) (*UserGroupListResponse, error) + ListAllUserGroup(filter string) (*UserGroupListResponse, error) + GetPermission(permissionUUID string) (*PermissionIntentResponse, error) + ListPermission(getEntitiesRequest *DSMetadata) (*PermissionListResponse, error) + ListAllPermission(filter string) (*PermissionListResponse, error) + GetProtectionRule(uuid string) (*ProtectionRuleResponse, error) + ListProtectionRules(getEntitiesRequest *DSMetadata) (*ProtectionRulesListResponse, error) + ListAllProtectionRules() (*ProtectionRulesListResponse, error) + CreateProtectionRule(request *ProtectionRuleInput) (*ProtectionRuleResponse, error) + UpdateProtectionRule(uuid string, body *ProtectionRuleInput) (*ProtectionRuleResponse, error) + DeleteProtectionRule(uuid string) (*DeleteResponse, error) + GetRecoveryPlan(uuid string) (*RecoveryPlanResponse, error) + ListRecoveryPlans(getEntitiesRequest *DSMetadata) (*RecoveryPlanListResponse, error) + ListAllRecoveryPlans() (*RecoveryPlanListResponse, error) + CreateRecoveryPlan(request *RecoveryPlanInput) (*RecoveryPlanResponse, error) + UpdateRecoveryPlan(uuid string, body *RecoveryPlanInput) (*RecoveryPlanResponse, error) + DeleteRecoveryPlan(uuid string) (*DeleteResponse, error) } /*CreateVM Creates a VM @@ -1447,3 +1477,678 @@ func (op Operations) DeleteAccessControlPolicy(uuid string) (*DeleteResponse, er return deleteResponse, op.client.Do(ctx, req, deleteResponse) } + +/*CreateRole creates a role + * This operation submits a request to create a role based on the input parameters. + * + * @param request *Role + * @return *Role + */ +func (op Operations) CreateRole(request *Role) (*Role, error) { + ctx := context.TODO() + + req, err := op.client.NewRequest(ctx, http.MethodPost, "/roles", request) + if err != nil { + return nil, err + } + + RoleResponse := new(Role) + + return RoleResponse, op.client.Do(ctx, req, RoleResponse) +} + +/*GetRole This operation gets a role. + * + * @param uuid The role uuid - string. + * @return *Role + */ +func (op Operations) GetRole(roleUUID string) (*Role, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/roles/%s", roleUUID) + Role := new(Role) + + req, err := op.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + return Role, op.client.Do(ctx, req, Role) +} + +/*ListRole gets a list of roles. + * + * @param metadata allows create filters to get specific data - *DSMetadata. + * @return *RoleListResponse + */ +func (op Operations) ListRole(getEntitiesRequest *DSMetadata) (*RoleListResponse, error) { + ctx := context.TODO() + path := "/roles/list" + + RoleList := new(RoleListResponse) + + req, err := op.client.NewRequest(ctx, http.MethodPost, path, getEntitiesRequest) + if err != nil { + return nil, err + } + + return RoleList, op.client.Do(ctx, req, RoleList) +} + +/*ListAllRole gets a list of Roles + * This operation gets a list of Roles, allowing for sorting and pagination. + * Note: Entities that have not been created successfully are not listed. + * @return *RoleListResponse + */ +func (op Operations) ListAllRole(filter string) (*RoleListResponse, error) { + entities := make([]*Role, 0) + + resp, err := op.ListRole(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("role"), + Length: utils.Int64Ptr(itemsPerPage), + }) + + if err != nil { + return nil, err + } + + totalEntities := utils.Int64Value(resp.Metadata.TotalMatches) + remaining := totalEntities + offset := utils.Int64Value(resp.Metadata.Offset) + + if totalEntities > itemsPerPage { + for hasNext(&remaining) { + resp, err = op.ListRole(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("role"), + Length: utils.Int64Ptr(itemsPerPage), + Offset: utils.Int64Ptr(offset), + }) + + if err != nil { + return nil, err + } + + entities = append(entities, resp.Entities...) + + offset += itemsPerPage + log.Printf("[Debug] total=%d, remaining=%d, offset=%d len(entities)=%d\n", totalEntities, remaining, offset, len(entities)) + } + + resp.Entities = entities + } + + return resp, nil +} + +/*UpdateRole Updates a role + * This operation submits a request to update a existing role based on the input parameters + * @param uuid The uuid of the entity - string. + * @param body - *Role + * @return *Role, error + */ +func (op Operations) UpdateRole(uuid string, body *Role) (*Role, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/roles/%s", uuid) + RoleInput := new(Role) + + req, err := op.client.NewRequest(ctx, http.MethodPut, path, body) + if err != nil { + return nil, err + } + + return RoleInput, op.client.Do(ctx, req, RoleInput) +} + +/*DeleteRole Deletes a role + * This operation submits a request to delete a existing role. + * + * @param uuid The uuid of the entity. + * @return void + */ +func (op Operations) DeleteRole(uuid string) (*DeleteResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/roles/%s", uuid) + + req, err := op.client.NewRequest(ctx, http.MethodDelete, path, nil) + deleteResponse := new(DeleteResponse) + + if err != nil { + return nil, err + } + + return deleteResponse, op.client.Do(ctx, req, deleteResponse) +} + +/*CreateUser creates a User + * This operation submits a request to create a userbased on the input parameters. + * + * @param request *VMIntentInput + * @return *UserIntentResponse + */ +func (op Operations) CreateUser(request *UserIntentInput) (*UserIntentResponse, error) { + ctx := context.TODO() + + req, err := op.client.NewRequest(ctx, http.MethodPost, "/users", request) + if err != nil { + return nil, err + } + + UserIntentResponse := new(UserIntentResponse) + + return UserIntentResponse, op.client.Do(ctx, req, UserIntentResponse) +} + +/*GetUser This operation gets a User. + * + * @param uuid The user uuid - string. + * @return *User + */ +func (op Operations) GetUser(userUUID string) (*UserIntentResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/users/%s", userUUID) + User := new(UserIntentResponse) + + req, err := op.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + return User, op.client.Do(ctx, req, User) +} + +/*UpdateUser Updates a User + * This operation submits a request to update a existing User based on the input parameters + * @param uuid The uuid of the entity - string. + * @param body - *User + * @return *User, error + */ +func (op Operations) UpdateUser(uuid string, body *UserIntentInput) (*UserIntentResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/users/%s", uuid) + UserInput := new(UserIntentResponse) + + req, err := op.client.NewRequest(ctx, http.MethodPut, path, body) + if err != nil { + return nil, err + } + + return UserInput, op.client.Do(ctx, req, UserInput) +} + +/*DeleteUser Deletes a User + * This operation submits a request to delete a existing User. + * + * @param uuid The uuid of the entity. + * @return void + */ +func (op Operations) DeleteUser(uuid string) (*DeleteResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/users/%s", uuid) + + req, err := op.client.NewRequest(ctx, http.MethodDelete, path, nil) + deleteResponse := new(DeleteResponse) + + if err != nil { + return nil, err + } + + return deleteResponse, op.client.Do(ctx, req, deleteResponse) +} + +/*ListUser gets a list of Users. + * + * @param metadata allows create filters to get specific data - *DSMetadata. + * @return *UserListResponse + */ +func (op Operations) ListUser(getEntitiesRequest *DSMetadata) (*UserListResponse, error) { + ctx := context.TODO() + path := "/users/list" + + UserList := new(UserListResponse) + + req, err := op.client.NewRequest(ctx, http.MethodPost, path, getEntitiesRequest) + if err != nil { + return nil, err + } + + return UserList, op.client.Do(ctx, req, UserList) +} + +// ListAllUser ... +func (op Operations) ListAllUser(filter string) (*UserListResponse, error) { + entities := make([]*UserIntentResponse, 0) + + resp, err := op.ListUser(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("user"), + Length: utils.Int64Ptr(itemsPerPage), + }) + + if err != nil { + return nil, err + } + + totalEntities := utils.Int64Value(resp.Metadata.TotalMatches) + remaining := totalEntities + offset := utils.Int64Value(resp.Metadata.Offset) + + if totalEntities > itemsPerPage { + for hasNext(&remaining) { + resp, err = op.ListUser(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("user"), + Length: utils.Int64Ptr(itemsPerPage), + Offset: utils.Int64Ptr(offset), + }) + + if err != nil { + return nil, err + } + + entities = append(entities, resp.Entities...) + + offset += itemsPerPage + } + + resp.Entities = entities + } + + return resp, nil +} + +/*GetUserGroup This operation gets a User. + * + * @param uuid The user uuid - string. + * @return *User + */ +func (op Operations) GetUserGroup(userGroupUUID string) (*UserGroupIntentResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/user_groups/%s", userGroupUUID) + User := new(UserGroupIntentResponse) + + req, err := op.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + return User, op.client.Do(ctx, req, User) +} + +/*ListUserGroup gets a list of UserGroups. + * + * @param metadata allows create filters to get specific data - *DSMetadata. + * @return *UserGroupListResponse + */ +func (op Operations) ListUserGroup(getEntitiesRequest *DSMetadata) (*UserGroupListResponse, error) { + ctx := context.TODO() + path := "/user_groups/list" + + UserGroupList := new(UserGroupListResponse) + + req, err := op.client.NewRequest(ctx, http.MethodPost, path, getEntitiesRequest) + if err != nil { + return nil, err + } + + return UserGroupList, op.client.Do(ctx, req, UserGroupList) +} + +// ListAllUserGroup ... +func (op Operations) ListAllUserGroup(filter string) (*UserGroupListResponse, error) { + entities := make([]*UserGroupIntentResponse, 0) + + resp, err := op.ListUserGroup(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("user_group"), + Length: utils.Int64Ptr(itemsPerPage), + }) + + if err != nil { + return nil, err + } + + totalEntities := utils.Int64Value(resp.Metadata.TotalMatches) + remaining := totalEntities + offset := utils.Int64Value(resp.Metadata.Offset) + + if totalEntities > itemsPerPage { + for hasNext(&remaining) { + resp, err = op.ListUserGroup(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("user"), + Length: utils.Int64Ptr(itemsPerPage), + Offset: utils.Int64Ptr(offset), + }) + + if err != nil { + return nil, err + } + + entities = append(entities, resp.Entities...) + + offset += itemsPerPage + } + + resp.Entities = entities + } + + return resp, nil +} + +/*GePermission This operation gets a Permission. + * + * @param uuid The permission uuid - string. + * @return *PermissionIntentResponse + */ +func (op Operations) GetPermission(permissionUUID string) (*PermissionIntentResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/permissions/%s", permissionUUID) + permission := new(PermissionIntentResponse) + + req, err := op.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + return permission, op.client.Do(ctx, req, permission) +} + +/*ListPermission gets a list of Permissions. + * + * @param metadata allows create filters to get specific data - *DSMetadata. + * @return *PermissionListResponse + */ +func (op Operations) ListPermission(getEntitiesRequest *DSMetadata) (*PermissionListResponse, error) { + ctx := context.TODO() + path := "/permissions/list" + + PermissionList := new(PermissionListResponse) + + req, err := op.client.NewRequest(ctx, http.MethodPost, path, getEntitiesRequest) + if err != nil { + return nil, err + } + + return PermissionList, op.client.Do(ctx, req, PermissionList) +} + +// ListAllPermission ... +func (op Operations) ListAllPermission(filter string) (*PermissionListResponse, error) { + entities := make([]*PermissionIntentResponse, 0) + + resp, err := op.ListPermission(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("permission"), + Length: utils.Int64Ptr(itemsPerPage), + }) + + if err != nil { + return nil, err + } + + totalEntities := utils.Int64Value(resp.Metadata.TotalMatches) + remaining := totalEntities + offset := utils.Int64Value(resp.Metadata.Offset) + + if totalEntities > itemsPerPage { + for hasNext(&remaining) { + resp, err = op.ListPermission(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("permission"), + Length: utils.Int64Ptr(itemsPerPage), + Offset: utils.Int64Ptr(offset), + }) + + if err != nil { + return nil, err + } + + entities = append(entities, resp.Entities...) + + offset += itemsPerPage + } + + resp.Entities = entities + } + + return resp, nil +} + +//GetProtectionRule ... +func (op Operations) GetProtectionRule(uuid string) (*ProtectionRuleResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/protection_rules/%s", uuid) + protectionRule := new(ProtectionRuleResponse) + + req, err := op.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + return protectionRule, op.client.Do(ctx, req, protectionRule) +} + +//ListProtectionRules ... +func (op Operations) ListProtectionRules(getEntitiesRequest *DSMetadata) (*ProtectionRulesListResponse, error) { + ctx := context.TODO() + path := "/protection_rules/list" + + list := new(ProtectionRulesListResponse) + + req, err := op.client.NewRequest(ctx, http.MethodPost, path, getEntitiesRequest) + if err != nil { + return nil, err + } + + return list, op.client.Do(ctx, req, list) +} + +// ListAllProtectionRules ... +func (op Operations) ListAllProtectionRules() (*ProtectionRulesListResponse, error) { + entities := make([]*ProtectionRuleResponse, 0) + + resp, err := op.ListProtectionRules(&DSMetadata{ + Kind: utils.StringPtr("protection_rule"), + Length: utils.Int64Ptr(itemsPerPage), + }) + if err != nil { + return nil, err + } + + totalEntities := utils.Int64Value(resp.Metadata.TotalMatches) + remaining := totalEntities + offset := utils.Int64Value(resp.Metadata.Offset) + + if totalEntities > itemsPerPage { + for hasNext(&remaining) { + resp, err = op.ListProtectionRules(&DSMetadata{ + Kind: utils.StringPtr("protection_rule"), + Length: utils.Int64Ptr(itemsPerPage), + Offset: utils.Int64Ptr(offset), + }) + + if err != nil { + return nil, err + } + + entities = append(entities, resp.Entities...) + + offset += itemsPerPage + log.Printf("[Debug] total=%d, remaining=%d, offset=%d len(entities)=%d\n", totalEntities, remaining, offset, len(entities)) + } + + resp.Entities = entities + } + + return resp, nil +} + +//CreateProtectionRule ... +func (op Operations) CreateProtectionRule(createRequest *ProtectionRuleInput) (*ProtectionRuleResponse, error) { + ctx := context.TODO() + + req, err := op.client.NewRequest(ctx, http.MethodPost, "/protection_rules", createRequest) + protectionRuleResponse := new(ProtectionRuleResponse) + + if err != nil { + return nil, err + } + + return protectionRuleResponse, op.client.Do(ctx, req, protectionRuleResponse) +} + +//UpdateProtectionRule ... +func (op Operations) UpdateProtectionRule(uuid string, body *ProtectionRuleInput) (*ProtectionRuleResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/protection_rules/%s", uuid) + req, err := op.client.NewRequest(ctx, http.MethodPut, path, body) + protectionRuleResponse := new(ProtectionRuleResponse) + + if err != nil { + return nil, err + } + + return protectionRuleResponse, op.client.Do(ctx, req, protectionRuleResponse) +} + +//DeleteProtectionRule ... +func (op Operations) DeleteProtectionRule(uuid string) (*DeleteResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/protection_rules/%s", uuid) + + req, err := op.client.NewRequest(ctx, http.MethodDelete, path, nil) + deleteResponse := new(DeleteResponse) + + if err != nil { + return nil, err + } + + return deleteResponse, op.client.Do(ctx, req, deleteResponse) +} + +//GetRecoveryPlan ... +func (op Operations) GetRecoveryPlan(uuid string) (*RecoveryPlanResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/recovery_plans/%s", uuid) + RecoveryPlan := new(RecoveryPlanResponse) + + req, err := op.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + return RecoveryPlan, op.client.Do(ctx, req, RecoveryPlan) +} + +//ListRecoveryPlans ... +func (op Operations) ListRecoveryPlans(getEntitiesRequest *DSMetadata) (*RecoveryPlanListResponse, error) { + ctx := context.TODO() + path := "/recovery_plans/list" + + list := new(RecoveryPlanListResponse) + + req, err := op.client.NewRequest(ctx, http.MethodPost, path, getEntitiesRequest) + if err != nil { + return nil, err + } + + return list, op.client.Do(ctx, req, list) +} + +// ListAllRecoveryPlans ... +func (op Operations) ListAllRecoveryPlans() (*RecoveryPlanListResponse, error) { + entities := make([]*RecoveryPlanResponse, 0) + + resp, err := op.ListRecoveryPlans(&DSMetadata{ + Kind: utils.StringPtr("recovery_plan"), + Length: utils.Int64Ptr(itemsPerPage), + }) + if err != nil { + return nil, err + } + + totalEntities := utils.Int64Value(resp.Metadata.TotalMatches) + remaining := totalEntities + offset := utils.Int64Value(resp.Metadata.Offset) + + if totalEntities > itemsPerPage { + for hasNext(&remaining) { + resp, err = op.ListRecoveryPlans(&DSMetadata{ + Kind: utils.StringPtr("recovery_plan"), + Length: utils.Int64Ptr(itemsPerPage), + Offset: utils.Int64Ptr(offset), + }) + + if err != nil { + return nil, err + } + + entities = append(entities, resp.Entities...) + + offset += itemsPerPage + log.Printf("[Debug] total=%d, remaining=%d, offset=%d len(entities)=%d\n", totalEntities, remaining, offset, len(entities)) + } + + resp.Entities = entities + } + + return resp, nil +} + +//CreateRecoveryPlan ... +func (op Operations) CreateRecoveryPlan(createRequest *RecoveryPlanInput) (*RecoveryPlanResponse, error) { + ctx := context.TODO() + + req, err := op.client.NewRequest(ctx, http.MethodPost, "/recovery_plans", createRequest) + RecoveryPlanResponse := new(RecoveryPlanResponse) + + if err != nil { + return nil, err + } + + return RecoveryPlanResponse, op.client.Do(ctx, req, RecoveryPlanResponse) +} + +//UpdateRecoveryPlan ... +func (op Operations) UpdateRecoveryPlan(uuid string, body *RecoveryPlanInput) (*RecoveryPlanResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/recovery_plans/%s", uuid) + req, err := op.client.NewRequest(ctx, http.MethodPut, path, body) + RecoveryPlanResponse := new(RecoveryPlanResponse) + + if err != nil { + return nil, err + } + + return RecoveryPlanResponse, op.client.Do(ctx, req, RecoveryPlanResponse) +} + +//DeleteRecoveryPlan ... +func (op Operations) DeleteRecoveryPlan(uuid string) (*DeleteResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/recovery_plans/%s", uuid) + + req, err := op.client.NewRequest(ctx, http.MethodDelete, path, nil) + deleteResponse := new(DeleteResponse) + + if err != nil { + return nil, err + } + + return deleteResponse, op.client.Do(ctx, req, deleteResponse) +} diff --git a/client/v3/v3_service_test.go b/client/v3/v3_service_test.go index 05523b2b3..93bcdb742 100644 --- a/client/v3/v3_service_test.go +++ b/client/v3/v3_service_test.go @@ -3,6 +3,7 @@ package v3 import ( "encoding/json" "fmt" + "github.com/stretchr/testify/assert" "io/ioutil" "net/http" "net/http/httptest" @@ -10,8 +11,6 @@ import ( "reflect" "testing" - "github.com/stretchr/testify/assert" - "github.com/terraform-providers/terraform-provider-nutanix/client" "github.com/terraform-providers/terraform-provider-nutanix/utils" ) @@ -3768,3 +3767,2311 @@ func TestOperations_DeleteAccessControlPolicy(t *testing.T) { }) } } + +func TestOperations_CreateRole(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/roles", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + + expected := map[string]interface{}{ + "api_version": "3.1", + "metadata": map[string]interface{}{ + "name": "role_test_name", + "kind": "role", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "spec": map[string]interface{}{ + "resources": map[string]interface{}{ + "permission_reference_list": []interface{}{ + map[string]interface{}{ + "name": "role_test_name", + "kind": "role", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + }, + }, + "name": "role_name", + "description": "description_test", + }, + } + + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + + if !reflect.DeepEqual(v, expected) { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + fmt.Fprintf(w, `{ + "api_version": "3.1", + "metadata": { + "kind": "role", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc" + } + }`) + }) + + type fields struct { + client *client.Client + } + + type args struct { + request *Role + } + + tests := []struct { + name string + fields fields + args args + want *Role + wantErr bool + }{ + { + "Test CreateRole", + fields{c}, + args{ + &Role{ + APIVersion: "3.1", + Metadata: &Metadata{ + Name: utils.StringPtr("role_test_name"), + Kind: utils.StringPtr("role"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + Spec: &RoleSpec{ + Name: utils.StringPtr("role_name"), + Description: utils.StringPtr("description_test"), + Resources: &RoleResources{ + PermissionReferenceList: []*Reference{ + { + Kind: utils.StringPtr("role"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + Name: utils.StringPtr("role_test_name"), + }, + }, + }, + }, + }, + }, + &Role{ + APIVersion: "3.1", + Metadata: &Metadata{ + Kind: utils.StringPtr("role"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + }, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.CreateRole(tt.args.request) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.CreateRole() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.CreateRole() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_GetRole(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/roles/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodGet) + fmt.Fprint(w, `{"metadata": {"kind":"host","uuid":"cfde831a-4e87-4a75-960f-89b0148aa2cc"}}`) + }) + + hostResponse := &Role{} + hostResponse.Metadata = &Metadata{ + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + Kind: utils.StringPtr("host"), + } + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + } + + tests := []struct { + name string + fields fields + args args + want *Role + wantErr bool + }{ + { + "Test GetRole OK", + fields{c}, + args{"cfde831a-4e87-4a75-960f-89b0148aa2cc"}, + hostResponse, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.GetRole(tt.args.UUID) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.GetRole() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.GetRole() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_ListRole(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/roles/list", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + fmt.Fprint(w, `{"entities":[{"metadata": {"kind":"host","uuid":"cfde831a-4e87-4a75-960f-89b0148aa2cc"}}]}`) + }) + + hostList := &RoleListResponse{} + hostList.Entities = make([]*Role, 1) + hostList.Entities[0] = &Role{} + hostList.Entities[0].Metadata = &Metadata{ + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + Kind: utils.StringPtr("host"), + } + + input := &DSMetadata{ + Length: utils.Int64Ptr(1.0), + } + + type fields struct { + client *client.Client + } + + type args struct { + getEntitiesRequest *DSMetadata + } + + tests := []struct { + name string + fields fields + args args + want *RoleListResponse + wantErr bool + }{ + { + "Test ListSubnet OK", + fields{c}, + args{input}, + hostList, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.ListRole(tt.args.getEntitiesRequest) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.ListRole() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.ListRole() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_UpdateRole(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/roles/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPut) + + expected := map[string]interface{}{ + "metadata": map[string]interface{}{ + "name": "role_test_name", + "kind": "role", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "spec": map[string]interface{}{ + "resources": map[string]interface{}{ + "permission_reference_list": []interface{}{ + map[string]interface{}{ + "name": "role_test_name", + "kind": "role", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + }, + }, + "name": "role_name", + "description": "description_test", + }, + } + + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + + if !reflect.DeepEqual(v, expected) { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + fmt.Fprintf(w, `{ + "api_version": "3.1", + "metadata": { + "kind": "role", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc" + } + }`) + }) + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + body *Role + } + + tests := []struct { + name string + fields fields + args args + want *Role + wantErr bool + }{ + { + "Test CreateRole", + fields{c}, + args{ + "cfde831a-4e87-4a75-960f-89b0148aa2cc", + &Role{ + Metadata: &Metadata{ + Name: utils.StringPtr("role_test_name"), + Kind: utils.StringPtr("role"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + Spec: &RoleSpec{ + Name: utils.StringPtr("role_name"), + Description: utils.StringPtr("description_test"), + Resources: &RoleResources{ + PermissionReferenceList: []*Reference{ + { + Kind: utils.StringPtr("role"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + Name: utils.StringPtr("role_test_name"), + }, + }, + }, + }, + }, + }, + &Role{ + APIVersion: "3.1", + Metadata: &Metadata{ + Kind: utils.StringPtr("role"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + }, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.UpdateRole(tt.args.UUID, tt.args.body) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.UpdateRole() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.UpdateRole() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_DeleteRole(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/roles/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodDelete) + + fmt.Fprintf(w, `{ + "status": { + "state": "DELETE_PENDING", + "execution_context": { + "task_uuid": "ff1b9547-dc9a-4ebd-a2ff-f2b718af935e" + } + }, + "spec": "", + "api_version": "3.1", + "metadata": { + "kind": "role", + "categories": { + "Project": "default" + } + } + }`) + }) + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + } + + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + "Test DeleteRole OK", + fields{c}, + args{"cfde831a-4e87-4a75-960f-89b0148aa2cc"}, + false, + }, + + { + "Test DeleteRole Errored", + fields{c}, + args{}, + true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + if _, err := op.DeleteRole(tt.args.UUID); (err != nil) != tt.wantErr { + t.Errorf("Operations.DeleteRole() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestOperations_CreateUser(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/users", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + + expected := map[string]interface{}{ + "api_version": "3.1", + "metadata": map[string]interface{}{ + "name": "user_name", + "kind": "user", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "spec": map[string]interface{}{ + "resources": map[string]interface{}{ + "directory_service_user": map[string]interface{}{ + "directory_service_reference": map[string]interface{}{ + "kind": "directory_service", + "uuid": "d8b53470-c432-4556-badd-a11c937d89c9", + }, + "user_principal_name": "user-dummy-tbd@ntnx.local", + }, + }, + }, + } + + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + + if !reflect.DeepEqual(v, expected) { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + fmt.Fprintf(w, `{ + "api_version": "3.1", + "metadata": { + "kind": "user", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc" + } + }`) + }) + + type fields struct { + client *client.Client + } + + type args struct { + request *UserIntentInput + } + + tests := []struct { + name string + fields fields + args args + want *UserIntentResponse + wantErr bool + }{ + { + "Test CreateUser", + fields{c}, + args{ + &UserIntentInput{ + APIVersion: utils.StringPtr("3.1"), + Metadata: &Metadata{ + Name: utils.StringPtr("user_name"), + Kind: utils.StringPtr("user"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + Spec: &UserSpec{ + Resources: &UserResources{ + DirectoryServiceUser: &DirectoryServiceUser{ + DirectoryServiceReference: &Reference{ + Kind: utils.StringPtr("directory_service"), + UUID: utils.StringPtr("d8b53470-c432-4556-badd-a11c937d89c9"), + }, + UserPrincipalName: utils.StringPtr("user-dummy-tbd@ntnx.local"), + }, + }, + }, + }, + }, + &UserIntentResponse{ + APIVersion: utils.StringPtr("3.1"), + Metadata: &Metadata{ + Kind: utils.StringPtr("user"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + }, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.CreateUser(tt.args.request) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.CreateUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.CreateUser() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_GetUser(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/users/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodGet) + fmt.Fprint(w, `{"metadata": {"kind":"user","uuid":"cfde831a-4e87-4a75-960f-89b0148aa2cc"}}`) + }) + + hostResponse := &UserIntentResponse{} + hostResponse.Metadata = &Metadata{ + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + Kind: utils.StringPtr("user"), + } + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + } + + tests := []struct { + name string + fields fields + args args + want *UserIntentResponse + wantErr bool + }{ + { + "Test GetUser OK", + fields{c}, + args{"cfde831a-4e87-4a75-960f-89b0148aa2cc"}, + hostResponse, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.GetUser(tt.args.UUID) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.GetUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.GetUser() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_ListUser(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/users/list", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + fmt.Fprint(w, `{"entities":[{"metadata": {"kind":"user","uuid":"cfde831a-4e87-4a75-960f-89b0148aa2cc"}}]}`) + }) + + hostList := &UserListResponse{} + hostList.Entities = make([]*UserIntentResponse, 1) + hostList.Entities[0] = &UserIntentResponse{} + hostList.Entities[0].Metadata = &Metadata{ + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + Kind: utils.StringPtr("user"), + } + + input := &DSMetadata{ + Length: utils.Int64Ptr(1.0), + } + + type fields struct { + client *client.Client + } + + type args struct { + getEntitiesRequest *DSMetadata + } + + tests := []struct { + name string + fields fields + args args + want *UserListResponse + wantErr bool + }{ + { + "Test ListUser OK", + fields{c}, + args{input}, + hostList, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.ListUser(tt.args.getEntitiesRequest) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.ListUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.ListUser() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_UpdateUser(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/users/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPut) + + expected := map[string]interface{}{ + "api_version": "3.1", + "metadata": map[string]interface{}{ + "name": "user_name", + "kind": "user", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "spec": map[string]interface{}{ + "resources": map[string]interface{}{ + "directory_service_user": map[string]interface{}{ + "directory_service_reference": map[string]interface{}{ + "kind": "directory_service", + "uuid": "d8b53470-c432-4556-badd-a11c937d89c9", + }, + "user_principal_name": "user-dummy-tbd@ntnx.local", + }, + }, + }, + } + + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + + if !reflect.DeepEqual(v, expected) { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + fmt.Fprintf(w, `{ + "api_version": "3.1", + "metadata": { + "kind": "user", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc" + } + }`) + }) + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + body *UserIntentInput + } + + tests := []struct { + name string + fields fields + args args + want *UserIntentResponse + wantErr bool + }{ + { + "Test CreateUser", + fields{c}, + args{ + "cfde831a-4e87-4a75-960f-89b0148aa2cc", + &UserIntentInput{ + APIVersion: utils.StringPtr("3.1"), + Metadata: &Metadata{ + Name: utils.StringPtr("user_name"), + Kind: utils.StringPtr("user"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + Spec: &UserSpec{ + Resources: &UserResources{ + DirectoryServiceUser: &DirectoryServiceUser{ + DirectoryServiceReference: &Reference{ + Kind: utils.StringPtr("directory_service"), + UUID: utils.StringPtr("d8b53470-c432-4556-badd-a11c937d89c9"), + }, + UserPrincipalName: utils.StringPtr("user-dummy-tbd@ntnx.local"), + }, + }, + }, + }, + }, + &UserIntentResponse{ + APIVersion: utils.StringPtr("3.1"), + Metadata: &Metadata{ + Kind: utils.StringPtr("user"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + }, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.UpdateUser(tt.args.UUID, tt.args.body) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.UpdateUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.UpdateUser() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_DeleteUser(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/users/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodDelete) + + fmt.Fprintf(w, `{ + "status": { + "state": "DELETE_PENDING", + "execution_context": { + "task_uuid": "ff1b9547-dc9a-4ebd-a2ff-f2b718af935e" + } + }, + "spec": "", + "api_version": "3.1", + "metadata": { + "kind": "user", + "categories": { + "Project": "default" + } + } + }`) + }) + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + } + + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + "Test DeleteUser OK", + fields{c}, + args{"cfde831a-4e87-4a75-960f-89b0148aa2cc"}, + false, + }, + + { + "Test DeleteUser Errored", + fields{c}, + args{}, + true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + if _, err := op.DeleteUser(tt.args.UUID); (err != nil) != tt.wantErr { + t.Errorf("Operations.DeleteUser() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestOperations_CreateProtectionRule(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/protection_rules", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + + expected := map[string]interface{}{ + "api_version": "3.1", + "metadata": map[string]interface{}{ + "name": "protection_rule_test_name", + "kind": "protection_rule", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "spec": map[string]interface{}{ + "resources": map[string]interface{}{ + "start_time": "00h:00m", + "ordered_availability_zone_list": []interface{}{ + map[string]interface{}{ + "availability_zone_url": "url test", + "cluster_uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + }, + "availability_zone_connectivity_list": []interface{}{ + map[string]interface{}{ + "destination_availability_zone_index": float64(0), + "source_availability_zone_index": float64(0), + "snapshot_schedule_list": []interface{}{ + map[string]interface{}{ + "recovery_point_objective_secs": float64(0), + "auto_suspend_timeout_secs": float64(0), + "snapshot_type": "CRASH_CONSISTENT", + "local_snapshot_retention_policy": map[string]interface{}{ + "num_snapshots": float64(1), + "rollup_retention_policy": map[string]interface{}{ + "snapshot_interval_type": "HOURLY", + "multiple": float64(1), + }, + }, + "remote_snapshot_retention_policy": map[string]interface{}{ + "num_snapshots": float64(1), + "rollup_retention_policy": map[string]interface{}{ + "snapshot_interval_type": "HOURLY", + "multiple": float64(1), + }, + }, + }, + }, + }, + }, + "category_filter": map[string]interface{}{ + "type": "CATEGORIES_MATCH_ALL", + "kind_list": []interface{}{"1", "2"}, + }, + }, + "name": "protection_rule_name", + "description": "description_test", + }, + } + + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + + assert := assert.New(t) + if !assert.Equal(v, expected, "The response should be the same") { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + fmt.Fprintf(w, `{ + "api_version": "3.1", + "metadata": { + "kind": "protection_rule", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc" + } + }`) + }) + + type fields struct { + client *client.Client + } + + type args struct { + request *ProtectionRuleInput + } + + tests := []struct { + name string + fields fields + args args + want *ProtectionRuleResponse + wantErr bool + }{ + { + "Test CreateProtectionRule", + fields{c}, + args{ + &ProtectionRuleInput{ + APIVersion: "3.1", + Metadata: &Metadata{ + Name: utils.StringPtr("protection_rule_test_name"), + Kind: utils.StringPtr("protection_rule"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + Spec: &ProtectionRuleSpec{ + Name: "protection_rule_name", + Description: "description_test", + Resources: &ProtectionRuleResources{ + StartTime: "00h:00m", + OrderedAvailabilityZoneList: []*OrderedAvailabilityZoneList{ + { + AvailabilityZoneURL: "url test", + ClusterUUID: "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + }, + AvailabilityZoneConnectivityList: []*AvailabilityZoneConnectivityList{ + { + DestinationAvailabilityZoneIndex: utils.Int64Ptr(0), + SourceAvailabilityZoneIndex: utils.Int64Ptr(0), + SnapshotScheduleList: []*SnapshotScheduleList{ + { + RecoveryPointObjectiveSecs: utils.Int64Ptr(0), + AutoSuspendTimeoutSecs: utils.Int64Ptr(0), + SnapshotType: "CRASH_CONSISTENT", + LocalSnapshotRetentionPolicy: &SnapshotRetentionPolicy{ + NumSnapshots: utils.Int64Ptr(1), + RollupRetentionPolicy: &RollupRetentionPolicy{ + SnapshotIntervalType: "HOURLY", + Multiple: utils.Int64Ptr(1), + }, + }, + RemoteSnapshotRetentionPolicy: &SnapshotRetentionPolicy{ + NumSnapshots: utils.Int64Ptr(1), + RollupRetentionPolicy: &RollupRetentionPolicy{ + SnapshotIntervalType: "HOURLY", + Multiple: utils.Int64Ptr(1), + }, + }, + }, + }, + }, + }, + CategoryFilter: &CategoryFilter{ + Type: utils.StringPtr("CATEGORIES_MATCH_ALL"), + KindList: []*string{utils.StringPtr("1"), utils.StringPtr("2")}, + }, + }, + }, + }, + }, + &ProtectionRuleResponse{ + APIVersion: "3.1", + Metadata: &Metadata{ + Kind: utils.StringPtr("protection_rule"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + }, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.CreateProtectionRule(tt.args.request) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.CreateProtectionRule() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.CreateProtectionRule() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_GetProtectionRule(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/protection_rules/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodGet) + fmt.Fprint(w, `{"metadata": {"kind":"protection_rule","uuid":"cfde831a-4e87-4a75-960f-89b0148aa2cc"}}`) + }) + + response := &ProtectionRuleResponse{} + response.Metadata = &Metadata{ + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + Kind: utils.StringPtr("protection_rule"), + } + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + } + + tests := []struct { + name string + fields fields + args args + want *ProtectionRuleResponse + wantErr bool + }{ + { + "Test GetProtectionRules OK", + fields{c}, + args{"cfde831a-4e87-4a75-960f-89b0148aa2cc"}, + response, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.GetProtectionRule(tt.args.UUID) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.GetProtectionRules() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.GetProtectionRules() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_ListProtectionRules(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/protection_rules/list", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + fmt.Fprint(w, `{"entities":[{"metadata": {"kind":"protection_rule","uuid":"cfde831a-4e87-4a75-960f-89b0148aa2cc"}}]}`) + }) + + responseList := &ProtectionRulesListResponse{} + responseList.Entities = make([]*ProtectionRuleResponse, 1) + responseList.Entities[0] = &ProtectionRuleResponse{} + responseList.Entities[0].Metadata = &Metadata{ + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + Kind: utils.StringPtr("protection_rule"), + } + + input := &DSMetadata{ + Length: utils.Int64Ptr(1.0), + } + + type fields struct { + client *client.Client + } + + type args struct { + getEntitiesRequest *DSMetadata + } + + tests := []struct { + name string + fields fields + args args + want *ProtectionRulesListResponse + wantErr bool + }{ + { + "Test ListProtectionRules OK", + fields{c}, + args{input}, + responseList, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.ListProtectionRules(tt.args.getEntitiesRequest) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.ListProtectionRules() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.ListProtectionRules() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_UpdateProtectionRules(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/protection_rules/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPut) + + expected := map[string]interface{}{ + "api_version": "3.1", + "metadata": map[string]interface{}{ + "name": "protection_rule_test_name", + "kind": "protection_rule", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "spec": map[string]interface{}{ + "resources": map[string]interface{}{ + "start_time": "00h:00m", + "ordered_availability_zone_list": []interface{}{ + map[string]interface{}{ + "availability_zone_url": "url test", + "cluster_uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + }, + "availability_zone_connectivity_list": []interface{}{ + map[string]interface{}{ + "destination_availability_zone_index": float64(0), + "source_availability_zone_index": float64(0), + "snapshot_schedule_list": []interface{}{ + map[string]interface{}{ + "recovery_point_objective_secs": float64(0), + "auto_suspend_timeout_secs": float64(0), + "snapshot_type": "CRASH_CONSISTENT", + "local_snapshot_retention_policy": map[string]interface{}{ + "num_snapshots": float64(1), + "rollup_retention_policy": map[string]interface{}{ + "snapshot_interval_type": "HOURLY", + "multiple": float64(1), + }, + }, + "remote_snapshot_retention_policy": map[string]interface{}{ + "num_snapshots": float64(1), + "rollup_retention_policy": map[string]interface{}{ + "snapshot_interval_type": "HOURLY", + "multiple": float64(1), + }, + }, + }, + }, + }, + }, + "category_filter": map[string]interface{}{ + "type": "CATEGORIES_MATCH_ALL", + "kind_list": []interface{}{"1", "2"}, + }, + }, + "name": "protection_rule_name", + "description": "description_test", + }, + } + + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + + assert := assert.New(t) + if !assert.Equal(v, expected, "The response should be the same") { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + fmt.Fprintf(w, `{ + "api_version": "3.1", + "metadata": { + "kind": "protection_rule", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc" + } + }`) + }) + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + body *ProtectionRuleInput + } + + tests := []struct { + name string + fields fields + args args + want *ProtectionRuleResponse + wantErr bool + }{ + { + "Test CreateProtectionRule", + fields{c}, + args{ + "cfde831a-4e87-4a75-960f-89b0148aa2cc", + &ProtectionRuleInput{ + APIVersion: "3.1", + Metadata: &Metadata{ + Name: utils.StringPtr("protection_rule_test_name"), + Kind: utils.StringPtr("protection_rule"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + Spec: &ProtectionRuleSpec{ + Name: "protection_rule_name", + Description: "description_test", + Resources: &ProtectionRuleResources{ + StartTime: "00h:00m", + OrderedAvailabilityZoneList: []*OrderedAvailabilityZoneList{ + { + AvailabilityZoneURL: "url test", + ClusterUUID: "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + }, + AvailabilityZoneConnectivityList: []*AvailabilityZoneConnectivityList{ + { + DestinationAvailabilityZoneIndex: utils.Int64Ptr(0), + SourceAvailabilityZoneIndex: utils.Int64Ptr(0), + SnapshotScheduleList: []*SnapshotScheduleList{ + { + RecoveryPointObjectiveSecs: utils.Int64Ptr(0), + AutoSuspendTimeoutSecs: utils.Int64Ptr(0), + SnapshotType: "CRASH_CONSISTENT", + LocalSnapshotRetentionPolicy: &SnapshotRetentionPolicy{ + NumSnapshots: utils.Int64Ptr(1), + RollupRetentionPolicy: &RollupRetentionPolicy{ + SnapshotIntervalType: "HOURLY", + Multiple: utils.Int64Ptr(1), + }, + }, + RemoteSnapshotRetentionPolicy: &SnapshotRetentionPolicy{ + NumSnapshots: utils.Int64Ptr(1), + RollupRetentionPolicy: &RollupRetentionPolicy{ + SnapshotIntervalType: "HOURLY", + Multiple: utils.Int64Ptr(1), + }, + }, + }, + }, + }, + }, + CategoryFilter: &CategoryFilter{ + Type: utils.StringPtr("CATEGORIES_MATCH_ALL"), + KindList: []*string{utils.StringPtr("1"), utils.StringPtr("2")}, + }, + }, + }, + }, + }, + &ProtectionRuleResponse{ + APIVersion: "3.1", + Metadata: &Metadata{ + Kind: utils.StringPtr("protection_rule"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + }, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.UpdateProtectionRule(tt.args.UUID, tt.args.body) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.UpdateProtectionRules() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.UpdateProtectionRules() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_DeleteProtectionRules(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/protection_rules/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodDelete) + }) + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + } + + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + "Test DeleteProtectionRules OK", + fields{c}, + args{"cfde831a-4e87-4a75-960f-89b0148aa2cc"}, + true, + }, + + { + "Test DeleteProtectionRules Errored", + fields{c}, + args{}, + true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + if _, err := op.DeleteProtectionRule(tt.args.UUID); (err != nil) != tt.wantErr { + t.Errorf("Operations.DeleteProtectionRules() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestOperations_CreateRecoveryPlan(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/recovery_plans", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + + expected := map[string]interface{}{ + "api_version": "3.1", + "metadata": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "spec": map[string]interface{}{ + "resources": map[string]interface{}{ + "parameters": map[string]interface{}{ + "network_mapping_list": []interface{}{ + map[string]interface{}{ + "are_networks_stretched": false, + "availability_zone_network_mapping_list": []interface{}{ + map[string]interface{}{ + "availability_zone_url": "zone url", + "recovery_network": map[string]interface{}{ + "use_vpc_reference": true, + "virtual_network_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "vpc_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "subnet_list": []interface{}{ + map[string]interface{}{ + "gateway_ip": "127.0.0.1", + "prefix_length": float64(16), + "external_connectivity_state": "external", + }, + }, + }, + "test_network": map[string]interface{}{ + "use_vpc_reference": true, + "virtual_network_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "vpc_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "subnet_list": []interface{}{ + map[string]interface{}{ + "gateway_ip": "127.0.0.1", + "prefix_length": float64(16), + "external_connectivity_state": "external", + }, + }, + }, + "recovery_ip_assignment_list": []interface{}{ + map[string]interface{}{ + "vm_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "ip_config_list": []interface{}{ + map[string]interface{}{ + "ip_address": "127.0.0.1", + }, + }, + }, + }, + "test_ip_assignment_list": []interface{}{ + map[string]interface{}{ + "vm_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "ip_config_list": []interface{}{ + map[string]interface{}{ + "ip_address": "127.0.0.1", + }, + }, + }, + }, + "cluster_reference_list": []interface{}{ + map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + }, + }, + }, + }, + }, + "floating_ip_assignment_list": []interface{}{ + map[string]interface{}{ + "availability_zone_url": "zone url", + "vm_ip_assignment_list": []interface{}{ + map[string]interface{}{ + "vm_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "vm_nic_information": map[string]interface{}{ + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + "ip": "127.0.0.1", + }, + "test_floating_ip_config": map[string]interface{}{ + "should_allocate_dynamically": false, + "ip": "127.0.0.1", + }, + "recovery_floating_ip_config": map[string]interface{}{ + "should_allocate_dynamically": false, + "ip": "127.0.0.1", + }, + }, + }, + }, + }, + }, + "stage_list": []interface{}{ + map[string]interface{}{ + "stage_uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + "delay_time_secs": float64(0), + "stage_work": map[string]interface{}{ + "recover_entities": map[string]interface{}{ + "entity_info_list": []interface{}{ + map[string]interface{}{ + "script_list": []interface{}{ + map[string]interface{}{ + "enable_script_exec": false, + "timeout": float64(0), + }, + }, + }, + }, + }, + }, + }, + }, + }, + "name": "recovery_plan_name", + "description": "description_test", + }, + } + + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + + assert := assert.New(t) + if !assert.Equal(v, expected, "The response should be the same") { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + fmt.Fprintf(w, `{ + "api_version": "3.1", + "metadata": { + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc" + } + }`) + }) + + type fields struct { + client *client.Client + } + + type args struct { + request *RecoveryPlanInput + } + + tests := []struct { + name string + fields fields + args args + want *RecoveryPlanResponse + wantErr bool + }{ + { + "Test CreateRecoveryPlans", + fields{c}, + args{ + &RecoveryPlanInput{ + APIVersion: "3.1", + Metadata: &Metadata{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + Spec: &RecoveryPlanSpec{ + Name: "recovery_plan_name", + Description: "description_test", + Resources: &RecoveryPlanResources{ + StageList: []*StageList{ + { + StageUUID: "cfde831a-4e87-4a75-960f-89b0148aa2cc", + DelayTimeSecs: utils.Int64Ptr(0), + StageWork: &StageWork{ + RecoverEntities: &RecoverEntities{ + EntityInfoList: []*EntityInfoList{ + { + ScriptList: []*ScriptList{ + { + EnableScriptExec: utils.BoolPtr(false), + Timeout: utils.Int64Ptr(0), + }, + }, + }, + }, + }, + }, + }, + }, + Parameters: &Parameters{ + FloatingIPAssignmentList: []*FloatingIPAssignmentList{ + { + AvailabilityZoneURL: "zone url", + VMIPAssignmentList: []*VMIPAssignmentList{ + { + VMReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + VMNICInformation: &VMNICInformation{ + UUID: "cfde831a-4e87-4a75-960f-89b0148aa2cc", + IP: "127.0.0.1", + }, + TestFloatingIPConfig: &FloatingIPConfig{ + IP: "127.0.0.1", + ShouldAllocateDynamically: utils.BoolPtr(false), + }, + RecoveryFloatingIPConfig: &FloatingIPConfig{ + IP: "127.0.0.1", + ShouldAllocateDynamically: utils.BoolPtr(false), + }, + }, + }, + }, + }, + NetworkMappingList: []*NetworkMappingList{ + { + AreNetworksStretched: utils.BoolPtr(false), + AvailabilityZoneNetworkMappingList: []*AvailabilityZoneNetworkMappingList{ + { + AvailabilityZoneURL: "zone url", + RecoveryNetwork: &Network{ + UseVPCReference: utils.BoolPtr(true), + VirtualNetworkReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + VPCReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + SubnetList: []*SubnetList{ + { + GatewayIP: "127.0.0.1", + PrefixLength: utils.Int64Ptr(16), + ExternalConnectivityState: "external", + }, + }, + }, + TestNetwork: &Network{ + UseVPCReference: utils.BoolPtr(true), + VirtualNetworkReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + VPCReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + SubnetList: []*SubnetList{ + { + GatewayIP: "127.0.0.1", + PrefixLength: utils.Int64Ptr(16), + ExternalConnectivityState: "external", + }, + }, + }, + RecoveryIPAssignmentList: []*IPAssignmentList{ + { + VMReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + IPConfigList: []*IPConfigList{ + { + IPAddress: "127.0.0.1", + }, + }, + }, + }, + TestIPAssignmentList: []*IPAssignmentList{ + { + VMReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + IPConfigList: []*IPConfigList{ + { + IPAddress: "127.0.0.1", + }, + }, + }, + }, + ClusterReferenceList: []*Reference{ + { + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &RecoveryPlanResponse{ + APIVersion: "3.1", + Metadata: &Metadata{ + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + }, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.CreateRecoveryPlan(tt.args.request) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.CreateRecoveryPlans() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.CreateRecoveryPlans() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_GetRecoveryPlan(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/recovery_plans/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodGet) + fmt.Fprint(w, `{"metadata": {"kind":"recovery_plan","uuid":"cfde831a-4e87-4a75-960f-89b0148aa2cc"}}`) + }) + + response := &RecoveryPlanResponse{} + response.Metadata = &Metadata{ + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + Kind: utils.StringPtr("recovery_plan"), + } + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + } + + tests := []struct { + name string + fields fields + args args + want *RecoveryPlanResponse + wantErr bool + }{ + { + "Test GetRecoveryPlan OK", + fields{c}, + args{"cfde831a-4e87-4a75-960f-89b0148aa2cc"}, + response, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.GetRecoveryPlan(tt.args.UUID) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.GetRecoveryPlan() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.GetRecoveryPlan() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_ListRecoveryPlans(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/recovery_plans/list", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + fmt.Fprint(w, `{"entities":[{"metadata": {"kind":"recovery_plan","uuid":"cfde831a-4e87-4a75-960f-89b0148aa2cc"}}]}`) + }) + + responseList := &RecoveryPlanListResponse{} + responseList.Entities = make([]*RecoveryPlanResponse, 1) + responseList.Entities[0] = &RecoveryPlanResponse{} + responseList.Entities[0].Metadata = &Metadata{ + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + Kind: utils.StringPtr("recovery_plan"), + } + + input := &DSMetadata{ + Length: utils.Int64Ptr(1.0), + } + + type fields struct { + client *client.Client + } + + type args struct { + getEntitiesRequest *DSMetadata + } + + tests := []struct { + name string + fields fields + args args + want *RecoveryPlanListResponse + wantErr bool + }{ + { + "Test ListRecoveryPlans OK", + fields{c}, + args{input}, + responseList, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.ListRecoveryPlans(tt.args.getEntitiesRequest) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.ListRecoveryPlans() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.ListRecoveryPlans() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_UpdateRecoveryPlans(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/recovery_plans/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPut) + + expected := map[string]interface{}{ + "api_version": "3.1", + "metadata": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "spec": map[string]interface{}{ + "resources": map[string]interface{}{ + "parameters": map[string]interface{}{ + "network_mapping_list": []interface{}{ + map[string]interface{}{ + "are_networks_stretched": false, + "availability_zone_network_mapping_list": []interface{}{ + map[string]interface{}{ + "availability_zone_url": "zone url", + "recovery_network": map[string]interface{}{ + "use_vpc_reference": true, + "virtual_network_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "vpc_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "subnet_list": []interface{}{ + map[string]interface{}{ + "gateway_ip": "127.0.0.1", + "prefix_length": float64(16), + "external_connectivity_state": "external", + }, + }, + }, + "test_network": map[string]interface{}{ + "use_vpc_reference": true, + "virtual_network_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "vpc_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "subnet_list": []interface{}{ + map[string]interface{}{ + "gateway_ip": "127.0.0.1", + "prefix_length": float64(16), + "external_connectivity_state": "external", + }, + }, + }, + "recovery_ip_assignment_list": []interface{}{ + map[string]interface{}{ + "vm_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "ip_config_list": []interface{}{ + map[string]interface{}{ + "ip_address": "127.0.0.1", + }, + }, + }, + }, + "test_ip_assignment_list": []interface{}{ + map[string]interface{}{ + "vm_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "ip_config_list": []interface{}{ + map[string]interface{}{ + "ip_address": "127.0.0.1", + }, + }, + }, + }, + "cluster_reference_list": []interface{}{ + map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + }, + }, + }, + }, + }, + "floating_ip_assignment_list": []interface{}{ + map[string]interface{}{ + "availability_zone_url": "zone url", + "vm_ip_assignment_list": []interface{}{ + map[string]interface{}{ + "vm_reference": map[string]interface{}{ + "name": "recovery_plan_test_name", + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + }, + "vm_nic_information": map[string]interface{}{ + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + "ip": "127.0.0.1", + }, + "test_floating_ip_config": map[string]interface{}{ + "should_allocate_dynamically": false, + "ip": "127.0.0.1", + }, + "recovery_floating_ip_config": map[string]interface{}{ + "should_allocate_dynamically": false, + "ip": "127.0.0.1", + }, + }, + }, + }, + }, + }, + "stage_list": []interface{}{ + map[string]interface{}{ + "stage_uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc", + "delay_time_secs": float64(0), + "stage_work": map[string]interface{}{ + "recover_entities": map[string]interface{}{ + "entity_info_list": []interface{}{ + map[string]interface{}{ + "script_list": []interface{}{ + map[string]interface{}{ + "enable_script_exec": false, + "timeout": float64(0), + }, + }, + }, + }, + }, + }, + }, + }, + }, + "name": "recovery_plan_name", + "description": "description_test", + }, + } + + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + + assert := assert.New(t) + if !assert.Equal(v, expected, "The response should be the same") { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + fmt.Fprintf(w, `{ + "api_version": "3.1", + "metadata": { + "kind": "recovery_plan", + "uuid": "cfde831a-4e87-4a75-960f-89b0148aa2cc" + } + }`) + }) + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + body *RecoveryPlanInput + } + + tests := []struct { + name string + fields fields + args args + want *RecoveryPlanResponse + wantErr bool + }{ + { + "Test UpdateRecoveryPlans", + fields{c}, + args{ + "cfde831a-4e87-4a75-960f-89b0148aa2cc", + &RecoveryPlanInput{ + APIVersion: "3.1", + Metadata: &Metadata{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + Spec: &RecoveryPlanSpec{ + Name: "recovery_plan_name", + Description: "description_test", + Resources: &RecoveryPlanResources{ + StageList: []*StageList{ + { + StageUUID: "cfde831a-4e87-4a75-960f-89b0148aa2cc", + DelayTimeSecs: utils.Int64Ptr(0), + StageWork: &StageWork{ + RecoverEntities: &RecoverEntities{ + EntityInfoList: []*EntityInfoList{ + { + ScriptList: []*ScriptList{ + { + EnableScriptExec: utils.BoolPtr(false), + Timeout: utils.Int64Ptr(0), + }, + }, + }, + }, + }, + }, + }, + }, + Parameters: &Parameters{ + FloatingIPAssignmentList: []*FloatingIPAssignmentList{ + { + AvailabilityZoneURL: "zone url", + VMIPAssignmentList: []*VMIPAssignmentList{ + { + VMReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + VMNICInformation: &VMNICInformation{ + UUID: "cfde831a-4e87-4a75-960f-89b0148aa2cc", + IP: "127.0.0.1", + }, + TestFloatingIPConfig: &FloatingIPConfig{ + IP: "127.0.0.1", + ShouldAllocateDynamically: utils.BoolPtr(false), + }, + RecoveryFloatingIPConfig: &FloatingIPConfig{ + IP: "127.0.0.1", + ShouldAllocateDynamically: utils.BoolPtr(false), + }, + }, + }, + }, + }, + NetworkMappingList: []*NetworkMappingList{ + { + AreNetworksStretched: utils.BoolPtr(false), + AvailabilityZoneNetworkMappingList: []*AvailabilityZoneNetworkMappingList{ + { + AvailabilityZoneURL: "zone url", + RecoveryNetwork: &Network{ + UseVPCReference: utils.BoolPtr(true), + VirtualNetworkReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + VPCReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + SubnetList: []*SubnetList{ + { + GatewayIP: "127.0.0.1", + PrefixLength: utils.Int64Ptr(16), + ExternalConnectivityState: "external", + }, + }, + }, + TestNetwork: &Network{ + UseVPCReference: utils.BoolPtr(true), + VirtualNetworkReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + VPCReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + SubnetList: []*SubnetList{ + { + GatewayIP: "127.0.0.1", + PrefixLength: utils.Int64Ptr(16), + ExternalConnectivityState: "external", + }, + }, + }, + RecoveryIPAssignmentList: []*IPAssignmentList{ + { + VMReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + IPConfigList: []*IPConfigList{ + { + IPAddress: "127.0.0.1", + }, + }, + }, + }, + TestIPAssignmentList: []*IPAssignmentList{ + { + VMReference: &Reference{ + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + IPConfigList: []*IPConfigList{ + { + IPAddress: "127.0.0.1", + }, + }, + }, + }, + ClusterReferenceList: []*Reference{ + { + Name: utils.StringPtr("recovery_plan_test_name"), + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &RecoveryPlanResponse{ + APIVersion: "3.1", + Metadata: &Metadata{ + Kind: utils.StringPtr("recovery_plan"), + UUID: utils.StringPtr("cfde831a-4e87-4a75-960f-89b0148aa2cc"), + }, + }, + false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + got, err := op.UpdateRecoveryPlan(tt.args.UUID, tt.args.body) + if (err != nil) != tt.wantErr { + t.Errorf("Operations.UpdateRecoveryPlans() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Operations.UpdateRecoveryPlans() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOperations_DeleteRecoveryPlan(t *testing.T) { + mux, c, server := setup() + + defer server.Close() + + mux.HandleFunc("/api/nutanix/v3/recovery_plans/cfde831a-4e87-4a75-960f-89b0148aa2cc", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodDelete) + }) + + type fields struct { + client *client.Client + } + + type args struct { + UUID string + } + + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + "Test DeleteRecoveryPlans OK", + fields{c}, + args{"cfde831a-4e87-4a75-960f-89b0148aa2cc"}, + true, + }, + + { + "Test DeleteRecoveryPlans Errored", + fields{c}, + args{}, + true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + op := Operations{ + client: tt.fields.client, + } + if _, err := op.DeleteRecoveryPlan(tt.args.UUID); (err != nil) != tt.wantErr { + t.Errorf("Operations.DeleteRecoveryPlan() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/client/v3/v3_structs.go b/client/v3/v3_structs.go index cd08ab2ac..16c4d2b67 100644 --- a/client/v3/v3_structs.go +++ b/client/v3/v3_structs.go @@ -1638,6 +1638,12 @@ type Metadata struct { // Applied on Prism Central only. Indicate whether force to translate the spec of the fanout request to fit the target cluster API schema. ShouldForceTranslate *bool `json:"should_force_translate,omitempty" mapstructure:"should_force_translate,omitempty"` + + //TODO: add if necessary + //CategoriesMapping map[string][]string `json:"categories_mapping,omitempty" mapstructure:"categories_mapping,omitempty"` + //EntityVersion *string `json:"entity_version,omitempty" mapstructure:"entity_version,omitempty"` + //UseCategoriesMapping *bool `json:"use_categories_mapping,omitempty" mapstructure:"use_categories_mapping,omitempty"` + } // NetworkSecurityRuleIntentInput An intentful representation of a network_security_rule @@ -2080,3 +2086,431 @@ type AccessControlPolicyListResponse struct { Entities []*AccessControlPolicy `json:"entities,omitempty"` Metadata *ListMetadataOutput `json:"metadata,omitempty"` } + +// RoleResources ... +type RoleResources struct { + PermissionReferenceList []*Reference `json:"permission_reference_list,omitempty"` +} + +// RoleStatus ... +type RoleStatus struct { + State *string `json:"state,omitempty"` + MessageList []*MessageResource `json:"message_list,omitempty"` + Name *string `json:"name,omitempty"` + Resources *RoleResources `json:"resources,omitempty"` + Description *string `json:"description,omitempty"` + ExecutionContext *ExecutionContext `json:"execution_context,omitempty"` +} + +// RoleSpec ... +type RoleSpec struct { + Name *string `json:"name,omitempty"` + Resources *RoleResources `json:"resources,omitempty"` + Description *string `json:"description,omitempty"` +} + +// Role Response object for intentful operations on a access policy +type Role struct { + Status *RoleStatus `json:"status,omitempty"` + Spec *RoleSpec `json:"spec,omitempty"` + APIVersion string `json:"api_version,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` +} + +// RoleListResponse Response object for intentful operation of access policy +type RoleListResponse struct { + APIVersion string `json:"api_version,omitempty"` + Entities []*Role `json:"entities,omitempty"` + Metadata *ListMetadataOutput `json:"metadata,omitempty"` +} + +type ResourceUsageSummary struct { + ResourceDomain *ResourceDomainStatus `json:"resource_domain"` // The status for a resource domain (limits and values) +} + +type ResourceDomainStatus struct { + Resources []ResourceUtilizationStatus `json:"resources,omitempty"` // The utilization/limit for resource types +} + +type ResourceUtilizationStatus struct { + Limit *int64 `json:"limit,omitempty"` // The resource consumption limit (unspecified is unlimited) + ResourceType *string `json:"resource_type,omitempty"` // The type of resource (for example storage, CPUs) + Units *string `json:"units,omitempty"` // The units of the resource type + Value *int64 `json:"value,omitempty"` // The amount of resource consumed +} + +// An intentful representation of a user +type UserIntentInput struct { + APIVersion *string `json:"api_version,omitempty"` // API Version of the Nutanix v3 API framework. + Metadata *Metadata `json:"metadata,omitempty"` // The user kind metadata + Spec *UserSpec `json:"spec,omitempty"` // User Input Definition. +} + +// Response object for intentful operations on a user +type UserIntentResponse struct { + APIVersion *string `json:"api_version,omitempty"` // API Version of the Nutanix v3 API framework. + Metadata *Metadata `json:"metadata,omitempty"` // The user kind metadata + Spec *UserSpec `json:"spec,omitempty"` // User Input Definition. + Status *UserStatus `json:"status,omitempty"` // User status definition. +} + +// User Input Definition. +type UserSpec struct { + Resources *UserResources `json:"resources,omitempty"` // User Resource Definition. +} + +// User Resource Definition. +type UserResources struct { + DirectoryServiceUser *DirectoryServiceUser `json:"directory_service_user,omitempty"` // A Directory Service user. + IdentityProviderUser *IdentityProvider `json:"identity_provider_user,omitempty"` // An Identity Provider user. +} + +// A Directory Service user. +type DirectoryServiceUser struct { + DefaultUserPrincipalName *string `json:"default_user_principal_name,omitempty"` // The Default UserPrincipalName of the user from the directory service. + DirectoryServiceReference *Reference `json:"directory_service_reference,omitempty"` // The reference to a directory_service + UserPrincipalName *string `json:"user_principal_name,omitempty"` // The UserPrincipalName of the user from the directory service. +} + +// An Identity Provider user. +type IdentityProvider struct { + IdentityProviderReference *Reference `json:"identity_provider_reference,omitempty"` // The reference to a identity_provider + Username *string `json:"username,omitempty"` // The username from the identity provider. Name Id for SAML Identity Provider. +} + +// User status definition. +type UserStatus struct { + MessageList []MessageResource `json:"message_list,omitempty"` + Name *string `json:"name,omitempty"` // Name of the User. + Resources *UserStatusResources `json:"resources,omitempty"` // User Resource Definition. + State *string `json:"state,omitempty"` // The state of the entity. + ExecutionContext *ExecutionContext `json:"execution_context,omitempty"` +} + +// User Resource Definition. +type UserStatusResources struct { + AccessControlPolicyReferenceList []*Reference `json:"access_control_policy_reference_list,omitempty"` // List of ACP references. + DirectoryServiceUser *DirectoryServiceUser `json:"directory_service_user,omitempty"` // A Directory Service user. + DisplayName *string `json:"display_name,omitempty"` // The display name of the user (common name) provided by the directory service. + IdentityProviderUser *IdentityProvider `json:"identity_provider_user,omitempty"` // An Identity Provider user. + ProjectsReferenceList []*Reference `json:"projects_reference_list,omitempty"` // A list of projects the user is part of. + ResourceUsageSummary *ResourceUsageSummary `json:"resource_usage_summary,omitempty"` + UserType *string `json:"user_type,omitempty"` +} + +type UserListResponse struct { + APIVersion *string `json:"api_version,omitempty"` // API Version of the Nutanix v3 API framework. + Entities []*UserIntentResponse `json:"entities,omitempty"` + Metadata *ListMetadataOutput `json:"metadata,omitempty"` // All api calls that return a list will have this metadata block +} + +// Response object for intentful operations on a user_group +type UserGroupIntentResponse struct { + APIVersion *string `json:"api_version,omitempty"` // API Version of the Nutanix v3 API framework. + Metadata *Metadata `json:"metadata,omitempty"` // The user_group kind metadata + Spec *UserGroupSpec `json:"spec,omitempty"` // User Group Input Definition. + Status *UserGroupStatus `json:"status,omitempty"` // User group status definition. +} + +// User Group Input Definition. +type UserGroupSpec struct { + Resources *UserGroupResources `json:"resources,omitempty"` // User Group Resource Definition +} + +// User Group Resource Definition +type UserGroupResources struct { + AccessControlPolicyReferenceList []*Reference `json:"access_control_policy_reference_list,omitempty"` // List of ACP references. + DirectoryServiceUserGroup *DirectoryServiceUserGroup `json:"directory_service_user_group,omitempty"` // A Directory Service user group. + DisplayName *string `json:"display_name,omitempty"` // The display name for the user group. + ProjectsReferenceList []*Reference `json:"projects_reference_list,omitempty"` // A list of projects the user group is part of. + UserGroupType *string `json:"user_group_type,omitempty"` +} + +// User group status definition. +type UserGroupStatus struct { + MessageList []MessageResource `json:"message_list,omitempty"` + Resources *UserGroupResources `json:"resources,omitempty"` // User Group Resource Definition. + State *string `json:"state,omitempty"` // The state of the entity. +} + +// A Directory Service user group. +type DirectoryServiceUserGroup struct { + DirectoryServiceReference *Reference `json:"directory_service_reference,omitempty"` // The reference to a directory_service + DistinguishedName *string `json:"distinguished_name,omitempty"` // The Distinguished name for the user group. +} + +// Response object for intentful operation of user_groups +type UserGroupListResponse struct { + APIVersion *string `json:"api_version,omitempty"` // API Version of the Nutanix v3 API framework. + Entities []*UserGroupIntentResponse `json:"entities,omitempty"` + Metadata *ListMetadataOutput `json:"metadata,omitempty"` // All api calls that return a list will have this metadata block +} + +// Response object for intentful operations on a user_group +type PermissionIntentResponse struct { + APIVersion *string `json:"api_version,omitempty"` // API Version of the Nutanix v3 API framework. + Metadata *Metadata `json:"metadata,omitempty"` // The user_group kind metadata + Spec *PermissionSpec `json:"spec,omitempty"` // Permission Input Definition. + Status *PermissionStatus `json:"status,omitempty"` // User group status definition. +} + +// Permission Input Definition. +type PermissionSpec struct { + Name *string `json:"name,omitempty"` // The name for the permission. + Description *string `json:"description,omitempty"` // The display name for the permission. + Resources *PermissionResources `json:"resources,omitempty"` // Permission Resource Definition +} + +// Permission Resource Definition +type PermissionResources struct { + Operation *string `json:"operation,omitempty"` + Kind *string `json:"kind,omitempty"` + Fields *FieldsPermission `json:"fields,omitempty"` +} + +type FieldsPermission struct { + FieldMode *string `json:"field_mode,omitempty"` + FieldNameList []*string `json:"field_name_list,omitempty"` +} + +// Permission status definition. +type PermissionStatus struct { + Name *string `json:"name,omitempty"` // The name for the permission. + Description *string `json:"description,omitempty"` // The display name for the permission. + Resources *PermissionResources `json:"resources,omitempty"` // Permission Resource Definition + MessageList []MessageResource `json:"message_list,omitempty"` + State *string `json:"state,omitempty"` // The state of the entity. +} + +// Response object for intentful operation of Permissions +type PermissionListResponse struct { + APIVersion *string `json:"api_version,omitempty"` // API Version of the Nutanix v3 API framework. + Entities []*PermissionIntentResponse `json:"entities,omitempty"` + Metadata *ListMetadataOutput `json:"metadata,omitempty"` // All api calls that return a list will have this metadata block +} + +//ProtectionRuleResources represents the resources of protection rules +type ProtectionRuleResources struct { + StartTime string `json:"start_time,omitempty"` + AvailabilityZoneConnectivityList []*AvailabilityZoneConnectivityList `json:"availability_zone_connectivity_list,omitempty"` + OrderedAvailabilityZoneList []*OrderedAvailabilityZoneList `json:"ordered_availability_zone_list,omitempty"` + CategoryFilter *CategoryFilter `json:"category_filter,omitempty"` +} + +//AvailabilityZoneConnectivityList represents a object for resource of protection rule +type AvailabilityZoneConnectivityList struct { + DestinationAvailabilityZoneIndex *int64 `json:"destination_availability_zone_index,omitempty"` + SourceAvailabilityZoneIndex *int64 `json:"source_availability_zone_index,omitempty"` + SnapshotScheduleList []*SnapshotScheduleList `json:"snapshot_schedule_list,omitempty"` +} + +//SnapshotScheduleList represents a object for resource of protection rule +type SnapshotScheduleList struct { + RecoveryPointObjectiveSecs *int64 `json:"recovery_point_objective_secs,omitempty"` + LocalSnapshotRetentionPolicy *SnapshotRetentionPolicy `json:"local_snapshot_retention_policy,omitempty"` + AutoSuspendTimeoutSecs *int64 `json:"auto_suspend_timeout_secs,omitempty"` + SnapshotType string `json:"snapshot_type,omitempty"` + RemoteSnapshotRetentionPolicy *SnapshotRetentionPolicy `json:"remote_snapshot_retention_policy,omitempty"` +} + +//SnapshotRetentionPolicy represents a object for resource of protection rule +type SnapshotRetentionPolicy struct { + NumSnapshots *int64 `json:"num_snapshots,omitempty"` + RollupRetentionPolicy *RollupRetentionPolicy `json:"rollup_retention_policy,omitempty"` +} + +//RollupRetentionPolicy represents a object for resource of protection rule +type RollupRetentionPolicy struct { + Multiple *int64 `json:"multiple,omitempty"` + SnapshotIntervalType string `json:"snapshot_interval_type,omitempty"` +} + +//OrderedAvailabilityZoneList represents a object for resource of protection rule +type OrderedAvailabilityZoneList struct { + ClusterUUID string `json:"cluster_uuid,omitempty"` + AvailabilityZoneURL string `json:"availability_zone_url,omitempty"` +} + +//ProtectionRuleStatus represents a status of a protection rule +type ProtectionRuleStatus struct { + State string `json:"state,omitempty"` + MessageList []*MessageResource `json:"message_list,omitempty"` + Name string `json:"name,omitempty"` + Resources *ProtectionRuleResources `json:"resources,omitempty"` + ExecutionContext *ExecutionContext `json:"execution_context,omitempty"` +} + +//ProtectionRuleSpec represents a spec of protection rules +type ProtectionRuleSpec struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Resources *ProtectionRuleResources `json:"resources,omitempty"` +} + +//ProtectionRuleResponse represents a response object of a protection rule +type ProtectionRuleResponse struct { + APIVersion string `json:"api_version,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` + Spec *ProtectionRuleSpec `json:"spec,omitempty"` + Status *ProtectionRuleStatus `json:"status,omitempty"` +} + +//ProtectionRulesListResponse represents the response of a list of protection rules +type ProtectionRulesListResponse struct { + APIVersion string `json:"api_version,omitempty"` + Entities []*ProtectionRuleResponse `json:"entities,omitempty"` + Metadata *ListMetadataOutput `json:"metadata,omitempty"` +} + +//ProtectionRuleInput Represents the request of create protection rule +type ProtectionRuleInput struct { + APIVersion string `json:"api_version,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` + Spec *ProtectionRuleSpec `json:"spec,omitempty"` +} + +//RecoveryPlanResources represents the resources of recovery plan +type RecoveryPlanResources struct { + StageList []*StageList `json:"stage_list,omitempty"` + Parameters *Parameters `json:"parameters,omitempty"` +} + +//Parameters represents a object for resource of recovery plan +type Parameters struct { + FloatingIPAssignmentList []*FloatingIPAssignmentList `json:"floating_ip_assignment_list,omitempty"` + NetworkMappingList []*NetworkMappingList `json:"network_mapping_list,omitempty"` +} + +//FloatingIPAssignmentList represents a object for resource of recovery plan +type FloatingIPAssignmentList struct { + AvailabilityZoneURL string `json:"availability_zone_url,omitempty"` + VMIPAssignmentList []*VMIPAssignmentList `json:"vm_ip_assignment_list,omitempty"` +} + +//VMIPAssignmentList represents a object for resource of recovery plan +type VMIPAssignmentList struct { + TestFloatingIPConfig *FloatingIPConfig `json:"test_floating_ip_config,omitempty"` + RecoveryFloatingIPConfig *FloatingIPConfig `json:"recovery_floating_ip_config,omitempty"` + VMReference *Reference `json:"vm_reference,omitempty"` + VMNICInformation *VMNICInformation `json:"vm_nic_information,omitempty"` +} + +//FloatingIPConfig represents a object for resource of recovery plan +type FloatingIPConfig struct { + IP string `json:"ip,omitempty"` + ShouldAllocateDynamically *bool `json:"should_allocate_dynamically,omitempty"` +} + +//VMNICInformation represents a object for resource of recovery plan +type VMNICInformation struct { + IP string `json:"ip,omitempty"` + UUID string `json:"uuid,omitempty"` +} + +// represents a object for resource of recovery plan +type NetworkMappingList struct { + AvailabilityZoneNetworkMappingList []*AvailabilityZoneNetworkMappingList `json:"availability_zone_network_mapping_list,omitempty"` + AreNetworksStretched *bool `json:"are_networks_stretched,omitempty"` +} + +//AvailabilityZoneNetworkMappingList represents a object for resource of recovery plan +type AvailabilityZoneNetworkMappingList struct { + RecoveryNetwork *Network `json:"recovery_network,omitempty"` + AvailabilityZoneURL string `json:"availability_zone_url,omitempty"` + TestNetwork *Network `json:"test_network,omitempty"` + RecoveryIPAssignmentList []*IPAssignmentList `json:"recovery_ip_assignment_list,omitempty"` + TestIPAssignmentList []*IPAssignmentList `json:"test_ip_assignment_list,omitempty"` + ClusterReferenceList []*Reference `json:"cluster_reference_list,omitempty"` +} + +type IPAssignmentList struct { + VMReference *Reference `json:"vm_reference,omitempty"` + IPConfigList []*IPConfigList `json:"ip_config_list,omitempty"` +} + +type IPConfigList struct { + IPAddress string `json:"ip_address,omitempty"` +} + +//Network represents a object for resource of recovery plan +type Network struct { + VirtualNetworkReference *Reference `json:"virtual_network_reference,omitempty"` + SubnetList []*SubnetList `json:"subnet_list,omitempty"` + Name string `json:"name,omitempty"` + VPCReference *Reference `json:"vpc_reference,omitempty"` + UseVPCReference *bool `json:"use_vpc_reference,omitempty"` +} + +//SubnetList represents a object for resource of recovery plan +type SubnetList struct { + GatewayIP string `json:"gateway_ip,omitempty"` + ExternalConnectivityState string `json:"external_connectivity_state,omitempty"` + PrefixLength *int64 `json:"prefix_length,omitempty"` +} + +//StageList represents a object for resource of recovery plan +type StageList struct { + StageWork *StageWork `json:"stage_work,omitempty"` + StageUUID string `json:"stage_uuid,omitempty"` + DelayTimeSecs *int64 `json:"delay_time_secs,omitempty"` +} + +//StageWork represents a object for resource of recovery plan +type StageWork struct { + RecoverEntities *RecoverEntities `json:"recover_entities,omitempty"` +} + +//RecoverEntities represents a object for resource of recovery plan +type RecoverEntities struct { + EntityInfoList []*EntityInfoList `json:"entity_info_list,omitempty"` +} + +//EntityInfoList represents a object for resource of recovery plan +type EntityInfoList struct { + AnyEntityReference *Reference `json:"any_entity_reference,omitempty"` + Categories map[string]string `json:"categories,omitempty"` + ScriptList []*ScriptList `json:"script_list,omitempty"` +} + +type ScriptList struct { + EnableScriptExec *bool `json:"enable_script_exec,omitempty"` + Timeout *int64 `json:"timeout,omitempty"` +} + +//RecoveryPlanStatus represents a status of a recovery plan +type RecoveryPlanStatus struct { + State string `json:"state,omitempty"` + MessageList []*MessageResource `json:"message_list,omitempty"` + Name string `json:"name,omitempty"` + Resources *RecoveryPlanResources `json:"resources,omitempty"` + ExecutionContext *ExecutionContext `json:"execution_context,omitempty"` +} + +//RecoveryPlanSpec represents a spec of recovery plans +type RecoveryPlanSpec struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Resources *RecoveryPlanResources `json:"resources,omitempty"` +} + +//RecoveryPlanResponse represents a response object of a recovery plan +type RecoveryPlanResponse struct { + APIVersion string `json:"api_version,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` + Spec *RecoveryPlanSpec `json:"spec,omitempty"` + Status *RecoveryPlanStatus `json:"status,omitempty"` +} + +//RecoveryPlanListResponse represents the response of a list of recovery plans +type RecoveryPlanListResponse struct { + APIVersion string `json:"api_version,omitempty"` + Entities []*RecoveryPlanResponse `json:"entities,omitempty"` + Metadata *ListMetadataOutput `json:"metadata,omitempty"` +} + +//RecoveryPlanInput Represents the request of create recovery plan +type RecoveryPlanInput struct { + APIVersion string `json:"api_version,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` + Spec *RecoveryPlanSpec `json:"spec,omitempty"` +} diff --git a/nutanix/data_source_nutanix_access_control_policies.go b/nutanix/data_source_nutanix_access_control_policies.go index a909f8b37..47b377b97 100644 --- a/nutanix/data_source_nutanix_access_control_policies.go +++ b/nutanix/data_source_nutanix_access_control_policies.go @@ -309,7 +309,7 @@ func dataSourceNutanixAccessControlPoliciesRead(d *schema.ResourceData, meta int metadata, filtersOk := d.GetOk("metadata") if filtersOk { - req = buildDataSourceSubnetsMetadata(metadata.(*schema.Set)) + req = buildDataSourceListMetadata(metadata.(*schema.Set)) } resp, err := conn.V3.ListAccessControlPolicy(req) diff --git a/nutanix/data_source_nutanix_access_control_policy_test.go b/nutanix/data_source_nutanix_access_control_policy_test.go index 968457b44..28cd4cdde 100644 --- a/nutanix/data_source_nutanix_access_control_policy_test.go +++ b/nutanix/data_source_nutanix_access_control_policy_test.go @@ -12,14 +12,13 @@ import ( func TestAccNutanixAccessControlPolicyDataSource_basic(t *testing.T) { name := acctest.RandomWithPrefix("accest-access-policy") description := "Description of my access control policy" - uuidRole := "760dac6c-be97-4b24-adb0-e3c3026dc8d5" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccAccessControlPolicyDataSourceConfig(uuidRole, name, description), + Config: testAccAccessControlPolicyDataSourceConfig(name, description), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( "data.nutanix_access_control_policy.test", "name", name), @@ -32,19 +31,27 @@ func TestAccNutanixAccessControlPolicyDataSource_basic(t *testing.T) { }) } -func testAccAccessControlPolicyDataSourceConfig(uuidRole, name, description string) string { +func testAccAccessControlPolicyDataSourceConfig(name, description string) string { return fmt.Sprintf(` +resource "nutanix_role" "test" { + name = "test role" + description = "description role" + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } +} resource "nutanix_access_control_policy" "test" { name = "%[1]s" description = "%[2]s" role_reference{ kind = "role" - uuid = "%[3]s" + uuid = nutanix_role.test.id } } data "nutanix_access_control_policy" "test" { access_control_policy_id = nutanix_access_control_policy.test.id } -`, name, description, uuidRole) +`, name, description) } diff --git a/nutanix/data_source_nutanix_permission.go b/nutanix/data_source_nutanix_permission.go new file mode 100644 index 000000000..bfa4ca0a1 --- /dev/null +++ b/nutanix/data_source_nutanix_permission.go @@ -0,0 +1,256 @@ +package nutanix + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func dataSourceNutanixPermission() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNutanixPermissionRead, + Schema: map[string]*schema.Schema{ + "permission_id": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"permission_name"}, + }, + "permission_name": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"permission_id"}, + }, + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "last_update_time": { + Type: schema.TypeString, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "spec_version": { + Type: schema.TypeString, + Computed: true, + }, + "spec_hash": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "categories": categoriesSchema(), + "owner_reference": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "project_reference": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "operation": { + Type: schema.TypeString, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "fields": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_mode": { + Type: schema.TypeString, + Computed: true, + }, + "field_name_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceNutanixPermissionRead(d *schema.ResourceData, meta interface{}) error { + // Get client connection + conn := meta.(*Client).API + + permissionID, iok := d.GetOk("permission_id") + permissionName, rnOk := d.GetOk("permission_name") + + if !iok && !rnOk { + return fmt.Errorf("please provide `permission_id` or `permission_name`") + } + + var err error + var resp *v3.PermissionIntentResponse + + if iok { + resp, err = conn.V3.GetPermission(permissionID.(string)) + } + if rnOk { + resp, err = findPermissionByName(conn, permissionName.(string)) + } + utils.PrintToJSON(resp, "Permission: ") + if err != nil { + return err + } + + m, c := setRSEntityMetadata(resp.Metadata) + + if err := d.Set("metadata", m); err != nil { + return err + } + if err := d.Set("categories", c); err != nil { + return err + } + if err := d.Set("project_reference", flattenReferenceValues(resp.Metadata.ProjectReference)); err != nil { + return err + } + if err := d.Set("owner_reference", flattenReferenceValues(resp.Metadata.OwnerReference)); err != nil { + return err + } + if err := d.Set("api_version", resp.APIVersion); err != nil { + return err + } + + if status := resp.Status; status != nil { + if err := d.Set("name", utils.StringValue(resp.Status.Name)); err != nil { + return err + } + if err := d.Set("description", utils.StringValue(resp.Status.Description)); err != nil { + return err + } + if err := d.Set("state", utils.StringValue(resp.Status.State)); err != nil { + return err + } + + if res := status.Resources; res != nil { + if err := d.Set("operation", utils.StringValue(res.Operation)); err != nil { + return err + } + if err := d.Set("kind", utils.StringValue(res.Kind)); err != nil { + return err + } + if err := d.Set("fields", flattenFieldsPermission(res.Fields)); err != nil { + return err + } + } + } + d.SetId(utils.StringValue(resp.Metadata.UUID)) + + return nil +} + +func flattenFieldsPermission(fieldPermissions *v3.FieldsPermission) []map[string]interface{} { + flatFieldsPermissions := make([]map[string]interface{}, 0) + n := map[string]interface{}{ + "field_mode": fieldPermissions.FieldMode, + "field_name_list": fieldPermissions.FieldNameList, + } + flatFieldsPermissions = append(flatFieldsPermissions, n) + return flatFieldsPermissions +} + +func findPermissionByName(conn *v3.Client, name string) (*v3.PermissionIntentResponse, error) { + filter := fmt.Sprintf("name==%s", name) + resp, err := conn.V3.ListAllPermission(filter) + if err != nil { + return nil, err + } + + entities := resp.Entities + + found := make([]*v3.PermissionIntentResponse, 0) + for _, v := range entities { + if *v.Spec.Name == name { + found = append(found, v) + } + } + + if len(found) > 1 { + return nil, fmt.Errorf("your query returned more than one result. Please use permission_id argument instead") + } + + if len(found) == 0 { + return nil, fmt.Errorf("permission with the given name, not found") + } + found[0].APIVersion = resp.APIVersion + return found[0], nil +} diff --git a/nutanix/data_source_nutanix_permission_test.go b/nutanix/data_source_nutanix_permission_test.go new file mode 100644 index 000000000..d6b1ae970 --- /dev/null +++ b/nutanix/data_source_nutanix_permission_test.go @@ -0,0 +1,69 @@ +package nutanix + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +const ( + PERMISSIONNAME = "Access_Console_Virtual_Machine" + PERMISSINOUUID = "16b81a55-2bca-48c6-9fab-4f82c6bb4284" +) + +func TestAccNutanixPermissionDataSource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccPermissionDataSourceConfig(PERMISSINOUUID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.nutanix_permission.test", "name", PERMISSIONNAME), + resource.TestCheckResourceAttr( + "data.nutanix_permission.test", "operation", "console_access"), + resource.TestCheckResourceAttr( + "data.nutanix_permission.test", "fields.0.field_mode", "DISALLOWED"), + ), + }, + }, + }) +} + +func testAccPermissionDataSourceConfig(uuid string) string { + return fmt.Sprintf(` +data "nutanix_permission" "test" { + permission_id = "%s" +} +`, uuid) +} + +func TestAccNutanixPermissionDataSource_basicByName(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccPermissionDataSourceConfigByName(PERMISSIONNAME), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.nutanix_permission.test", "name", PERMISSIONNAME), + resource.TestCheckResourceAttr( + "data.nutanix_permission.test", "operation", "console_access"), + resource.TestCheckResourceAttr( + "data.nutanix_permission.test", "fields.0.field_mode", "DISALLOWED"), + ), + }, + }, + }) +} + +func testAccPermissionDataSourceConfigByName(name string) string { + return fmt.Sprintf(` +data "nutanix_permission" "test" { + permission_name = "%s" +} +`, name) +} diff --git a/nutanix/data_source_nutanix_permissions.go b/nutanix/data_source_nutanix_permissions.go new file mode 100644 index 000000000..902115353 --- /dev/null +++ b/nutanix/data_source_nutanix_permissions.go @@ -0,0 +1,235 @@ +package nutanix + +import ( + "log" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func dataSourceNutanixPermissions() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNutanixPermissionsRead, + Schema: map[string]*schema.Schema{ + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "sort_order": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "offset": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "length": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "sort_attribute": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "entities": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "last_update_time": { + Type: schema.TypeString, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "spec_version": { + Type: schema.TypeString, + Computed: true, + }, + "spec_hash": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "categories": categoriesSchema(), + "owner_reference": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "project_reference": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "operation": { + Type: schema.TypeString, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "fields": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_mode": { + Type: schema.TypeString, + Computed: true, + }, + "field_name_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceNutanixPermissionsRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Reading Permissions: %s", d.Id()) + + // Get client connection + conn := meta.(*Client).API + + resp, err := conn.V3.ListAllPermission("") + if err != nil { + return err + } + + if err := d.Set("api_version", resp.APIVersion); err != nil { + return err + } + + entities := make([]map[string]interface{}, len(resp.Entities)) + for k, v := range resp.Entities { + entity := make(map[string]interface{}) + + m, c := setRSEntityMetadata(v.Metadata) + + entity["metadata"] = m + entity["categories"] = c + entity["project_reference"] = flattenReferenceValues(v.Metadata.ProjectReference) + entity["owner_reference"] = flattenReferenceValues(v.Metadata.OwnerReference) + entity["api_version"] = utils.StringValue(v.APIVersion) + entity["state"] = utils.StringValue(v.Status.State) + entity["name"] = utils.StringValue(v.Status.Name) + entity["description"] = utils.StringValue(v.Status.Description) + entity["operation"] = utils.StringValue(v.Status.Resources.Operation) + entity["kind"] = utils.StringValue(v.Status.Resources.Kind) + entity["fields"] = flattenFieldsPermission(v.Status.Resources.Fields) + + entities[k] = entity + } + + if err := d.Set("entities", entities); err != nil { + return err + } + + d.SetId(resource.UniqueId()) + + return nil +} diff --git a/nutanix/data_source_nutanix_permissions_test.go b/nutanix/data_source_nutanix_permissions_test.go new file mode 100644 index 000000000..01a98555c --- /dev/null +++ b/nutanix/data_source_nutanix_permissions_test.go @@ -0,0 +1,30 @@ +package nutanix + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccNutanixPermissionsDataSource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccPermissionsDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.nutanix_permissions.test", "entities.#"), + resource.TestCheckResourceAttr( + "data.nutanix_permissions.test", "entities.#", "485"), + ), + }, + }, + }) +} + +func testAccPermissionsDataSourceConfig() string { + return ` +data "nutanix_permissions" "test" {} +` +} diff --git a/nutanix/data_source_nutanix_role.go b/nutanix/data_source_nutanix_role.go new file mode 100644 index 000000000..3e53ca742 --- /dev/null +++ b/nutanix/data_source_nutanix_role.go @@ -0,0 +1,232 @@ +package nutanix + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func dataSourceNutanixRole() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNutanixRoleRead, + Schema: map[string]*schema.Schema{ + "role_id": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"role_name"}, + }, + "role_name": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"role_id"}, + }, + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "last_update_time": { + Type: schema.TypeString, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "spec_version": { + Type: schema.TypeString, + Computed: true, + }, + "spec_hash": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "categories": categoriesSchema(), + "owner_reference": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "project_reference": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "permission_reference_list": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceNutanixRoleRead(d *schema.ResourceData, meta interface{}) error { + // Get client connection + conn := meta.(*Client).API + + accessID, iok := d.GetOk("role_id") + roleName, rnOk := d.GetOk("role_name") + + if !iok && !rnOk { + return fmt.Errorf("please provide `role_id` or `role_name`") + } + + var err error + var resp *v3.Role + + if iok { + resp, err = conn.V3.GetRole(accessID.(string)) + } + if rnOk { + resp, err = findRoleByName(conn, roleName.(string)) + } + + if err != nil { + return err + } + + m, c := setRSEntityMetadata(resp.Metadata) + + if err := d.Set("metadata", m); err != nil { + return err + } + if err := d.Set("categories", c); err != nil { + return err + } + if err := d.Set("project_reference", flattenReferenceValues(resp.Metadata.ProjectReference)); err != nil { + return err + } + if err := d.Set("owner_reference", flattenReferenceValues(resp.Metadata.OwnerReference)); err != nil { + return err + } + if err := d.Set("api_version", resp.APIVersion); err != nil { + return err + } + + if status := resp.Status; status != nil { + if err := d.Set("name", utils.StringValue(resp.Status.Name)); err != nil { + return err + } + if err := d.Set("description", utils.StringValue(resp.Status.Description)); err != nil { + return err + } + if err := d.Set("state", utils.StringValue(resp.Status.State)); err != nil { + return err + } + + if res := status.Resources; res != nil { + if err := d.Set("permission_reference_list", flattenArrayReferenceValues(status.Resources.PermissionReferenceList)); err != nil { + return err + } + } + } + d.SetId(utils.StringValue(resp.Metadata.UUID)) + + return nil +} + +func findRoleByName(conn *v3.Client, name string) (*v3.Role, error) { + filter := fmt.Sprintf("name==%s", name) + resp, err := conn.V3.ListAllRole(filter) + if err != nil { + return nil, err + } + + entities := resp.Entities + + found := make([]*v3.Role, 0) + for _, v := range entities { + if *v.Spec.Name == name { + found = append(found, v) + } + } + + if len(found) > 1 { + return nil, fmt.Errorf("your query returned more than one result. Please use role_id argument instead") + } + + if len(found) == 0 { + return nil, fmt.Errorf("role with the given name, not found") + } + + return found[0], nil +} diff --git a/nutanix/data_source_nutanix_role_test.go b/nutanix/data_source_nutanix_role_test.go new file mode 100644 index 000000000..573d6530c --- /dev/null +++ b/nutanix/data_source_nutanix_role_test.go @@ -0,0 +1,88 @@ +package nutanix + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccNutanixRoleDataSourceByID_basic(t *testing.T) { + name := acctest.RandomWithPrefix("accest-access-role") + description := "Description of my role" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccRoleDataSourceConfigByID(name, description), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.nutanix_role.test", "name", name), + resource.TestCheckResourceAttr( + "data.nutanix_role.test", "description", description), + resource.TestCheckResourceAttrSet("data.nutanix_role.test", "name"), + ), + }, + }, + }) +} + +func TestAccNutanixRoleDataSourceByName_basic(t *testing.T) { + name := acctest.RandomWithPrefix("accest-access-role") + description := "Description of my role" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccRoleDataSourceConfigByName(name, description), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.nutanix_role.test", "name", name), + resource.TestCheckResourceAttr( + "data.nutanix_role.test", "description", description), + resource.TestCheckResourceAttrSet("data.nutanix_role.test", "name"), + ), + }, + }, + }) +} + +func testAccRoleDataSourceConfigByID(name, description string) string { + return fmt.Sprintf(` +resource "nutanix_role" "test" { + name = "%[1]s" + description = "%[2]s" + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } +} + +data "nutanix_role" "test" { + role_id = nutanix_role.test.id +} +`, name, description) +} + +func testAccRoleDataSourceConfigByName(name, description string) string { + return fmt.Sprintf(` +resource "nutanix_role" "test" { + name = "%[1]s" + description = "%[2]s" + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } +} + +data "nutanix_role" "test" { + role_name = nutanix_role.test.name +} +`, name, description) +} diff --git a/nutanix/data_source_nutanix_roles.go b/nutanix/data_source_nutanix_roles.go new file mode 100644 index 000000000..f8b1d038b --- /dev/null +++ b/nutanix/data_source_nutanix_roles.go @@ -0,0 +1,236 @@ +package nutanix + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func dataSourceNutanixRoles() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNutanixRolesRead, + Schema: map[string]*schema.Schema{ + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "entities": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "role_id": { + Type: schema.TypeString, + Required: true, + }, + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "last_update_time": { + Type: schema.TypeString, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "spec_version": { + Type: schema.TypeString, + Computed: true, + }, + "spec_hash": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "categories": categoriesSchema(), + "owner_reference": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "project_reference": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "permission_reference_list": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "metadata": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "sort_order": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "offset": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "length": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "sort_attribute": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceNutanixRolesRead(d *schema.ResourceData, meta interface{}) error { + // Get client connection + conn := meta.(*Client).API + req := &v3.DSMetadata{} + + metadata, filtersOk := d.GetOk("metadata") + if filtersOk { + req = buildDataSourceListMetadata(metadata.(*schema.Set)) + } + + resp, err := conn.V3.ListRole(req) + if err != nil { + return err + } + + if err := d.Set("api_version", resp.APIVersion); err != nil { + return err + } + + entities := make([]map[string]interface{}, len(resp.Entities)) + for k, v := range resp.Entities { + entity := make(map[string]interface{}) + + m, c := setRSEntityMetadata(v.Metadata) + + entity["metadata"] = m + entity["project_reference"] = flattenReferenceValues(v.Metadata.ProjectReference) + entity["owner_reference"] = flattenReferenceValues(v.Metadata.OwnerReference) + entity["categories"] = c + entity["api_version"] = v.APIVersion + + if status := v.Status; status != nil { + entity["name"] = utils.StringValue(v.Status.Name) + entity["description"] = utils.StringValue(v.Status.State) + entity["state"] = utils.StringValue(v.Status.State) + if res := status.Resources; res != nil { + entity["permission_reference_list"] = flattenArrayReferenceValues(status.Resources.PermissionReferenceList) + } + } + + entities[k] = entity + } + + if err := d.Set("entities", entities); err != nil { + return err + } + + d.SetId(resource.UniqueId()) + + return nil +} diff --git a/nutanix/data_source_nutanix_roles_test.go b/nutanix/data_source_nutanix_roles_test.go new file mode 100644 index 000000000..eeb64cde9 --- /dev/null +++ b/nutanix/data_source_nutanix_roles_test.go @@ -0,0 +1,25 @@ +package nutanix + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccNutanixRolesDataSource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccRolesDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.nutanix_roles.test", "entities.0.name"), + ), + }, + }, + }) +} +func testAccRolesDataSourceConfig() string { + return `data "nutanix_roles" "test" {}` +} diff --git a/nutanix/data_source_nutanix_subnets.go b/nutanix/data_source_nutanix_subnets.go index 3987e6ff8..d2a4aead3 100644 --- a/nutanix/data_source_nutanix_subnets.go +++ b/nutanix/data_source_nutanix_subnets.go @@ -345,7 +345,7 @@ func dataSourceNutanixSubnetsRead(d *schema.ResourceData, meta interface{}) erro metadata, filtersOk := d.GetOk("metadata") if filtersOk { - req = buildDataSourceSubnetsMetadata(metadata.(*schema.Set)) + req = buildDataSourceListMetadata(metadata.(*schema.Set)) } resp, err := conn.V3.ListSubnet(req) @@ -448,7 +448,7 @@ func dataSourceNutanixSubnetsRead(d *schema.ResourceData, meta interface{}) erro return nil } -func buildDataSourceSubnetsMetadata(set *schema.Set) *v3.DSMetadata { +func buildDataSourceListMetadata(set *schema.Set) *v3.DSMetadata { filters := v3.DSMetadata{} for _, v := range set.List() { m := v.(map[string]interface{}) diff --git a/nutanix/data_source_nutanix_user.go b/nutanix/data_source_nutanix_user.go new file mode 100644 index 000000000..111db795f --- /dev/null +++ b/nutanix/data_source_nutanix_user.go @@ -0,0 +1,344 @@ +package nutanix + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func dataSourceNutanixUser() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNutanixUserRead, + Schema: map[string]*schema.Schema{ + "user_id": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"user_name"}, + }, + "user_name": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"user_id"}, + }, + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "last_update_time": { + Type: schema.TypeString, + Computed: true, + }, + + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "spec_version": { + Type: schema.TypeString, + Computed: true, + }, + "spec_hash": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "categories": categoriesSchema(), + "owner_reference": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "project_reference": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "directory_service_user": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_principal_name": { + Type: schema.TypeString, + Computed: true, + //ValidateFunc: validation.StringInSlice([]string{"role"}, false), + }, + "directory_service_reference": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "default_user_principal_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "identity_provider_user": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Computed: true, + }, + "identity_provider_reference": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "user_type": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "project_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "access_control_policy_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceNutanixUserRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Reading User: %s", d.Id()) + + // Get client connection + conn := meta.(*Client).API + + uuid, iok := d.GetOk("user_id") + name, nok := d.GetOk("user_name") + + if !iok && !nok { + return fmt.Errorf("please provide one of user_id or user_name attributes") + } + + var reqErr error + var resp *v3.UserIntentResponse + + if iok { + resp, reqErr = findUserByUUID(conn, uuid.(string)) + } else { + resp, reqErr = findUserByName(conn, name.(string)) + } + + if reqErr != nil { + if strings.Contains(fmt.Sprint(reqErr), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return fmt.Errorf("error reading user with error %s", reqErr) + } + + m, c := setRSEntityMetadata(resp.Metadata) + + if err := d.Set("metadata", m); err != nil { + return fmt.Errorf("error setting metadata for user UUID(%s), %s", d.Id(), err) + } + if err := d.Set("categories", c); err != nil { + return fmt.Errorf("error setting categories for user UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("owner_reference", flattenReferenceValues(resp.Metadata.OwnerReference)); err != nil { + return fmt.Errorf("error setting owner_reference for user UUID(%s), %s", d.Id(), err) + } + d.Set("api_version", utils.StringValue(resp.APIVersion)) + d.Set("name", utils.StringValue(resp.Status.Name)) + + if err := d.Set("state", resp.Status.State); err != nil { + return fmt.Errorf("error setting state for user UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("directory_service_user", flattenDirectoryServiceUser(resp.Status.Resources.DirectoryServiceUser)); err != nil { + return fmt.Errorf("error setting state for user UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("identity_provider_user", flattenIdentityProviderUser(resp.Status.Resources.IdentityProviderUser)); err != nil { + return fmt.Errorf("error setting state for user UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("user_type", resp.Status.Resources.UserType); err != nil { + return fmt.Errorf("error setting state for user UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("display_name", resp.Status.Resources.DisplayName); err != nil { + return fmt.Errorf("error setting state for user UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("project_reference_list", flattenArrayReferenceValues(resp.Status.Resources.ProjectsReferenceList)); err != nil { + return fmt.Errorf("error setting state for user UUID(%s), %s", d.Id(), err) + } + + refe := flattenArrayReferenceValues(resp.Status.Resources.AccessControlPolicyReferenceList) + utils.PrintToJSON(refe, "acceess") + + if err := d.Set("access_control_policy_reference_list", refe); err != nil { + return fmt.Errorf("error setting state for user UUID(%s), %s", d.Id(), err) + } + + d.SetId(*resp.Metadata.UUID) + + return nil +} + +func findUserByName(conn *v3.Client, name string) (*v3.UserIntentResponse, error) { + //filter := fmt.Sprintf("name==%s", name) + resp, err := conn.V3.ListAllUser("") + if err != nil { + return nil, err + } + + entities := resp.Entities + + found := make([]*v3.UserIntentResponse, 0) + for _, v := range entities { + if *v.Status.Name == name { + found = append(found, v) + } + } + + if len(found) > 1 { + return nil, fmt.Errorf("your query returned more than one result. Please use uuid argument instead") + } + + if len(found) == 0 { + return nil, fmt.Errorf("user with the given name, not found") + } + + return findUserByUUID(conn, *found[0].Metadata.UUID) +} + +func findUserByUUID(conn *v3.Client, uuid string) (*v3.UserIntentResponse, error) { + return conn.V3.GetUser(uuid) +} diff --git a/nutanix/data_source_nutanix_user_group.go b/nutanix/data_source_nutanix_user_group.go new file mode 100644 index 000000000..7c4c9ec64 --- /dev/null +++ b/nutanix/data_source_nutanix_user_group.go @@ -0,0 +1,365 @@ +package nutanix + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func dataSourceNutanixUserGroup() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNutanixUserGroupRead, + Schema: map[string]*schema.Schema{ + "user_group_id": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"user_group_name", "user_group_distinguished_name"}, + }, + "user_group_name": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"user_group_id", "user_group_distinguished_name"}, + }, + "user_group_distinguished_name": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"user_group_id", "user_group_name"}, + }, + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "last_update_time": { + Type: schema.TypeString, + Computed: true, + }, + + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "spec_version": { + Type: schema.TypeString, + Computed: true, + }, + "spec_hash": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "categories": categoriesSchema(), + "owner_reference": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "project_reference": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "user_group_type": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "directory_service_user_group": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "distinguished_name": { + Type: schema.TypeString, + Computed: true, + }, + "directory_service_reference": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "default_user_principal_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "project_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "access_control_policy_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceNutanixUserGroupRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Reading Group: %s", d.Id()) + + // Get client connection + conn := meta.(*Client).API + + uuid, iok := d.GetOk("user_group_id") + name, nok := d.GetOk("user_group_name") + dname, dnok := d.GetOk("user_group_distinguished_name") + + if !iok && !nok && !dnok { + return fmt.Errorf("please provide one of user_group_id, user_group_distinguished_name or user_group_name attributes") + } + + var reqErr error + var resp *v3.UserGroupIntentResponse + + if iok { + resp, reqErr = findUserGroupByUUID(conn, uuid.(string)) + } + + if dnok { + resp, reqErr = findUserGroupByDistinguishedName(conn, dname.(string)) + } + + if nok { + resp, reqErr = findUserGroupByName(conn, name.(string)) + } + + if reqErr != nil { + if strings.Contains(fmt.Sprint(reqErr), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return fmt.Errorf("error reading group with error %s", reqErr) + } + + m, c := setRSEntityMetadata(resp.Metadata) + + if err := d.Set("metadata", m); err != nil { + return fmt.Errorf("error setting metadata for group UUID(%s), %s", d.Id(), err) + } + if err := d.Set("categories", c); err != nil { + return fmt.Errorf("error setting categories for group UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("owner_reference", flattenReferenceValues(resp.Metadata.OwnerReference)); err != nil { + return fmt.Errorf("error setting owner_reference for group UUID(%s), %s", d.Id(), err) + } + d.Set("api_version", utils.StringValue(resp.APIVersion)) + d.Set("name", utils.StringValue(resp.Status.Resources.DisplayName)) + + if err := d.Set("state", resp.Status.State); err != nil { + return fmt.Errorf("error setting state for group UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("directory_service_user_group", flattenDirectoryServiceUserGroup(resp.Status.Resources.DirectoryServiceUserGroup)); err != nil { + return fmt.Errorf("error setting state for group UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("user_group_type", resp.Status.Resources.UserGroupType); err != nil { + return fmt.Errorf("error setting state for group UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("display_name", resp.Status.Resources.DisplayName); err != nil { + return fmt.Errorf("error setting state for group UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("project_reference_list", flattenArrayReferenceValues(resp.Status.Resources.ProjectsReferenceList)); err != nil { + return fmt.Errorf("error setting state for group UUID(%s), %s", d.Id(), err) + } + + refe := flattenArrayReferenceValues(resp.Status.Resources.AccessControlPolicyReferenceList) + + if err := d.Set("access_control_policy_reference_list", refe); err != nil { + return fmt.Errorf("error setting state for group UUID(%s), %s", d.Id(), err) + } + + d.SetId(*resp.Metadata.UUID) + + return nil +} + +func flattenDirectoryServiceUserGroup(dsu *v3.DirectoryServiceUserGroup) []interface{} { + if dsu != nil { + directoryServiceUserMap := map[string]interface{}{} + + if dsu.DistinguishedName != nil { + directoryServiceUserMap["distinguished_name"] = dsu.DistinguishedName + } + + if dsu.DirectoryServiceReference != nil { + directoryServiceUserMap["directory_service_reference"] = []interface{}{flattenReferenceValues(dsu.DirectoryServiceReference)} + } + return []interface{}{directoryServiceUserMap} + } + return nil +} + +func findUserGroupByName(conn *v3.Client, name string) (*v3.UserGroupIntentResponse, error) { + return findUserGroupByAttribute(conn, matchUserGroupByName, name) +} + +func findUserGroupByDistinguishedName(conn *v3.Client, name string) (*v3.UserGroupIntentResponse, error) { + return findUserGroupByAttribute(conn, matchUserGroupByDistinguishedName, name) +} + +func findUserGroupByAttribute(conn *v3.Client, matches func(*v3.UserGroupIntentResponse, string) bool, targetAttributeValue string) (*v3.UserGroupIntentResponse, error) { + //filter := fmt.Sprintf("name==%s", name) + resp, err := conn.V3.ListAllUserGroup("") + if err != nil { + return nil, err + } + + entities := resp.Entities + + found := make([]*v3.UserGroupIntentResponse, 0) + for _, v := range entities { + // if *v.Status.Resources.DisplayName == targetAttributeValue { + if matches(v, targetAttributeValue) { + found = append(found, v) + } + } + + if len(found) > 1 { + return nil, fmt.Errorf("your query returned more than one result. Please use uuid argument instead") + } + + if len(found) == 0 { + return nil, fmt.Errorf("user with the given name, not found") + } + + return findUserGroupByUUID(conn, *found[0].Metadata.UUID) +} + +func findUserGroupByUUID(conn *v3.Client, uuid string) (*v3.UserGroupIntentResponse, error) { + log.Printf("finding group via uuid: %s", uuid) + return conn.V3.GetUserGroup(uuid) +} + +func matchUserGroupByDistinguishedName(userGroup *v3.UserGroupIntentResponse, name string) bool { + if userGroup != nil && + userGroup.Status != nil && + userGroup.Status.Resources != nil && + userGroup.Status.Resources.DirectoryServiceUserGroup != nil && + userGroup.Status.Resources.DirectoryServiceUserGroup.DistinguishedName != nil && + *userGroup.Status.Resources.DirectoryServiceUserGroup.DistinguishedName == name { + return true + } + return false +} + +func matchUserGroupByName(userGroup *v3.UserGroupIntentResponse, name string) bool { + if userGroup != nil && + userGroup.Status != nil && + userGroup.Status.Resources != nil && + userGroup.Status.Resources.DisplayName != nil && + *userGroup.Status.Resources.DisplayName == name { + return true + } + return false +} diff --git a/nutanix/data_source_nutanix_user_group_test.go b/nutanix/data_source_nutanix_user_group_test.go new file mode 100644 index 000000000..498339977 --- /dev/null +++ b/nutanix/data_source_nutanix_user_group_test.go @@ -0,0 +1,104 @@ +package nutanix + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccNutanixUserGroupDataSource_basic(t *testing.T) { + distinguishedName := "cn=dou-group-1,cn=users,dc=ntnxlab,dc=local" + displayName := "dou-group-1" + uuid := "d12fa0a3-13f1-4f5d-b773-c8e2f8144f0e" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccUserGroupDataSourceConfig(uuid), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.nutanix_user_group.test", "display_name", displayName), + resource.TestCheckResourceAttrSet("data.nutanix_user_group.test", "directory_service_user_group.#"), + resource.TestCheckResourceAttr( + "data.nutanix_user_group.test", "directory_service_user_group.0.distinguished_name", distinguishedName), + ), + }, + }, + }) +} + +func testAccUserGroupDataSourceConfig(uuid string) string { + return fmt.Sprintf(` +data "nutanix_user_group" "test" { + user_group_id = "%s" +} +`, uuid) +} +func TestAccNutanixUserGroupDataSource_ByName(t *testing.T) { + distinguishedName := "cn=dou-group-1,cn=users,dc=ntnxlab,dc=local" + displayName := "dou-group-1" + uuid := "d12fa0a3-13f1-4f5d-b773-c8e2f8144f0e" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccUserGroupDataSourceConfigByName(displayName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.nutanix_user_group.test", "display_name", displayName), + resource.TestCheckResourceAttr( + "data.nutanix_user_group.test", "id", uuid), + resource.TestCheckResourceAttrSet("data.nutanix_user_group.test", "directory_service_user_group.#"), + resource.TestCheckResourceAttr( + "data.nutanix_user_group.test", "directory_service_user_group.0.distinguished_name", distinguishedName), + ), + }, + }, + }) +} + +func testAccUserGroupDataSourceConfigByName(dn string) string { + return fmt.Sprintf(` +data "nutanix_user_group" "test" { + user_group_name = "%s" +} +`, dn) +} + +func TestAccNutanixUserGroupDataSource_ByDistinguishedName(t *testing.T) { + distinguishedName := "cn=dou-group-1,cn=users,dc=ntnxlab,dc=local" + displayName := "dou-group-1" + uuid := "d12fa0a3-13f1-4f5d-b773-c8e2f8144f0e" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccUserGroupDataSourceConfigByDistinguishedName(distinguishedName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.nutanix_user_group.test", "display_name", displayName), + resource.TestCheckResourceAttr( + "data.nutanix_user_group.test", "id", uuid), + resource.TestCheckResourceAttrSet("data.nutanix_user_group.test", "directory_service_user_group.#"), + resource.TestCheckResourceAttr( + "data.nutanix_user_group.test", "directory_service_user_group.0.distinguished_name", distinguishedName), + ), + }, + }, + }) +} + +func testAccUserGroupDataSourceConfigByDistinguishedName(dn string) string { + return fmt.Sprintf(` +data "nutanix_user_group" "test" { + user_group_distinguished_name = "%s" +} +`, dn) +} diff --git a/nutanix/data_source_nutanix_user_groups.go b/nutanix/data_source_nutanix_user_groups.go new file mode 100644 index 000000000..107e5002f --- /dev/null +++ b/nutanix/data_source_nutanix_user_groups.go @@ -0,0 +1,291 @@ +package nutanix + +import ( + "log" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func dataSourceNutanixUserGroups() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNutanixUserGroupsRead, + Schema: map[string]*schema.Schema{ + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "sort_order": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "offset": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "length": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "sort_attribute": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "entities": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "last_update_time": { + Type: schema.TypeString, + Computed: true, + }, + + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "spec_version": { + Type: schema.TypeString, + Computed: true, + }, + "spec_hash": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "categories": categoriesSchema(), + "owner_reference": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "project_reference": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "user_group_type": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "directory_service_user_group": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "distinguished_name": { + Type: schema.TypeString, + Computed: true, + }, + "directory_service_reference": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "default_user_principal_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "project_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "access_control_policy_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceNutanixUserGroupsRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Reading User Groups: %s", d.Id()) + + // Get client connection + conn := meta.(*Client).API + + req := &v3.DSMetadata{} + + metadata, filtersOk := d.GetOk("metadata") + if filtersOk { + req = buildDataSourceListMetadata(metadata.(*schema.Set)) + } + + resp, err := conn.V3.ListUserGroup(req) + if err != nil { + return err + } + + if err := d.Set("api_version", resp.APIVersion); err != nil { + return err + } + + entities := make([]map[string]interface{}, len(resp.Entities)) + for k, v := range resp.Entities { + entity := make(map[string]interface{}) + + m, c := setRSEntityMetadata(v.Metadata) + + entity["metadata"] = m + entity["categories"] = c + entity["project_reference"] = flattenReferenceValues(v.Metadata.ProjectReference) + entity["owner_reference"] = flattenReferenceValues(v.Metadata.OwnerReference) + entity["api_version"] = utils.StringValue(v.APIVersion) + entity["state"] = utils.StringValue(v.Status.State) + entity["directory_service_user_group"] = flattenDirectoryServiceUserGroup(v.Status.Resources.DirectoryServiceUserGroup) + entity["user_group_type"] = utils.StringValue(v.Status.Resources.UserGroupType) + entity["display_name"] = utils.StringValue(v.Status.Resources.DisplayName) + entity["project_reference_list"] = flattenArrayReferenceValues(v.Status.Resources.ProjectsReferenceList) + entity["access_control_policy_reference_list"] = flattenArrayReferenceValues(v.Status.Resources.AccessControlPolicyReferenceList) + + entities[k] = entity + } + + if err := d.Set("entities", entities); err != nil { + return err + } + + d.SetId(resource.UniqueId()) + + return nil +} diff --git a/nutanix/data_source_nutanix_user_groups_test.go b/nutanix/data_source_nutanix_user_groups_test.go new file mode 100644 index 000000000..32c5faa8b --- /dev/null +++ b/nutanix/data_source_nutanix_user_groups_test.go @@ -0,0 +1,30 @@ +package nutanix + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccNutanixUserGroupsDataSource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccUserGroupsDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.nutanix_user_groups.test", "entities.#"), + resource.TestCheckResourceAttr( + "data.nutanix_user_groups.test", "entities.#", "3"), + ), + }, + }, + }) +} + +func testAccUserGroupsDataSourceConfig() string { + return ` +data "nutanix_user_groups" "test" {} +` +} diff --git a/nutanix/data_source_nutanix_user_test.go b/nutanix/data_source_nutanix_user_test.go new file mode 100644 index 000000000..074a85020 --- /dev/null +++ b/nutanix/data_source_nutanix_user_test.go @@ -0,0 +1,86 @@ +package nutanix + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccNutanixUserDataSource_basic(t *testing.T) { + principalName := "dou-user@ntnxlab.local" + expectedDisplayName := "dou-user" + directoryServiceUUID := "dd19a896-8e72-4158-b716-98455ceda220" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNutanixUserDestroy, + Steps: []resource.TestStep{ + { + Config: testAccUserDataSourceConfig(principalName, directoryServiceUUID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.nutanix_user.user", "display_name", expectedDisplayName), + resource.TestCheckResourceAttrSet("data.nutanix_user.user", "directory_service_user.#"), + ), + }, + }, + }) +} + +func testAccUserDataSourceConfig(pn, dsuuid string) string { + return fmt.Sprintf(` +resource "nutanix_user" "user" { + directory_service_user { + user_principal_name = "%s" + directory_service_reference { + uuid = "%s" + } + } +} + +data "nutanix_user" "user" { + user_id = nutanix_user.user.id +} +`, pn, dsuuid) +} + +func TestAccNutanixUserDataSource_byName(t *testing.T) { + principalName := "dou-user@ntnxlab.local" + expectedDisplayName := "dou-user" + directoryServiceUUID := "dd19a896-8e72-4158-b716-98455ceda220" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNutanixUserDestroy, + Steps: []resource.TestStep{ + { + Config: testAccUserDataSourceConfigByName(principalName, directoryServiceUUID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.nutanix_user.user", "display_name", expectedDisplayName), + resource.TestCheckResourceAttrSet("data.nutanix_user.user", "directory_service_user.#"), + ), + }, + }, + }) +} + +func testAccUserDataSourceConfigByName(pn, dsuuid string) string { + return fmt.Sprintf(` +resource "nutanix_user" "user" { + directory_service_user { + user_principal_name = "%s" + directory_service_reference { + uuid = "%s" + } + } +} + +data "nutanix_user" "user" { + user_name = nutanix_user.user.name +} +`, pn, dsuuid) +} diff --git a/nutanix/data_source_nutanix_users.go b/nutanix/data_source_nutanix_users.go new file mode 100644 index 000000000..c14e440ea --- /dev/null +++ b/nutanix/data_source_nutanix_users.go @@ -0,0 +1,332 @@ +package nutanix + +import ( + "log" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func dataSourceNutanixUsers() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNutanixUsersRead, + Schema: map[string]*schema.Schema{ + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "sort_order": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "offset": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "length": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "sort_attribute": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "entities": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "last_update_time": { + Type: schema.TypeString, + Computed: true, + }, + + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "spec_version": { + Type: schema.TypeString, + Computed: true, + }, + "spec_hash": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "categories": categoriesSchema(), + "owner_reference": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "project_reference": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "directory_service_user": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_principal_name": { + Type: schema.TypeString, + Computed: true, + //ValidateFunc: validation.StringInSlice([]string{"role"}, false), + }, + "directory_service_reference": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "default_user_principal_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "identity_provider_user": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Computed: true, + }, + "identity_provider_reference": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "user_type": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "project_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "access_control_policy_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceNutanixUsersRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Reading User: %s", d.Id()) + + // Get client connection + conn := meta.(*Client).API + + req := &v3.DSMetadata{} + + metadata, filtersOk := d.GetOk("metadata") + if filtersOk { + req = buildDataSourceListMetadata(metadata.(*schema.Set)) + } + + resp, err := conn.V3.ListUser(req) + if err != nil { + return err + } + + if err := d.Set("api_version", resp.APIVersion); err != nil { + return err + } + + entities := make([]map[string]interface{}, len(resp.Entities)) + for k, v := range resp.Entities { + entity := make(map[string]interface{}) + + m, c := setRSEntityMetadata(v.Metadata) + + entity["metadata"] = m + entity["categories"] = c + entity["project_reference"] = flattenReferenceValues(v.Metadata.ProjectReference) + entity["owner_reference"] = flattenReferenceValues(v.Metadata.OwnerReference) + entity["api_version"] = utils.StringValue(v.APIVersion) + entity["name"] = utils.StringValue(v.Status.Name) + entity["state"] = utils.StringValue(v.Status.State) + entity["directory_service_user"] = flattenDirectoryServiceUser(v.Status.Resources.DirectoryServiceUser) + entity["identity_provider_user"] = flattenIdentityProviderUser(v.Status.Resources.IdentityProviderUser) + entity["user_type"] = utils.StringValue(v.Status.Resources.UserType) + entity["display_name"] = utils.StringValue(v.Status.Resources.DisplayName) + entity["project_reference_list"] = flattenArrayReferenceValues(v.Status.Resources.ProjectsReferenceList) + entity["access_control_policy_reference_list"] = flattenArrayReferenceValues(v.Status.Resources.AccessControlPolicyReferenceList) + + entities[k] = entity + } + + if err := d.Set("entities", entities); err != nil { + return err + } + + d.SetId(resource.UniqueId()) + + return nil +} diff --git a/nutanix/helpers.go b/nutanix/helpers.go index df00b3049..bd11604e0 100644 --- a/nutanix/helpers.go +++ b/nutanix/helpers.go @@ -128,6 +128,31 @@ func validateRef(ref map[string]interface{}) *v3.Reference { return nil } +func expandReference(ref map[string]interface{}) *v3.Reference { + r := &v3.Reference{} + hasValue := false + + if v, ok := ref["kind"]; ok { + r.Kind = utils.StringPtr(v.(string)) + hasValue = true + } + + if v, ok := ref["uuid"]; ok { + r.UUID = utils.StringPtr(v.(string)) + hasValue = true + } + if v, ok := ref["name"]; ok { + r.Name = utils.StringPtr(v.(string)) + hasValue = true + } + + if hasValue { + return r + } + + return nil +} + func buildReference(uuid, kind string) *v3.Reference { return &v3.Reference{ Kind: utils.StringPtr(kind), diff --git a/nutanix/provider.go b/nutanix/provider.go index c4af86e4e..fd1492431 100644 --- a/nutanix/provider.go +++ b/nutanix/provider.go @@ -103,6 +103,14 @@ func Provider() terraform.ResourceProvider { "nutanix_protection_rules": dataSourceNutanixProtectionRules(), "nutanix_recovery_plan": dataSourceNutanixRecoveryPlan(), "nutanix_recovery_plans": dataSourceNutanixRecoveryPlans(), + "nutanix_role": dataSourceNutanixRole(), + "nutanix_roles": dataSourceNutanixRoles(), + "nutanix_user": dataSourceNutanixUser(), + "nutanix_users": dataSourceNutanixUsers(), + "nutanix_user_group": dataSourceNutanixUserGroup(), + "nutanix_user_groups": dataSourceNutanixUserGroups(), + "nutanix_permission": dataSourceNutanixPermission(), + "nutanix_permissions": dataSourceNutanixPermissions(), }, ResourcesMap: map[string]*schema.Resource{ "nutanix_virtual_machine": resourceNutanixVirtualMachine(), @@ -115,6 +123,8 @@ func Provider() terraform.ResourceProvider { "nutanix_project": resourceNutanixProject(), "nutanix_protection_rule": resourceNutanixProtectionRule(), "nutanix_recovery_plan": resourceNutanixRecoveryPlan(), + "nutanix_role": resourceNutanixRole(), + "nutanix_user": resourceNutanixUser(), }, ConfigureFunc: providerConfigure, } diff --git a/nutanix/resource_nutanix_access_control_policy_test.go b/nutanix/resource_nutanix_access_control_policy_test.go index 6acfd7d1d..0db6642fc 100644 --- a/nutanix/resource_nutanix_access_control_policy_test.go +++ b/nutanix/resource_nutanix_access_control_policy_test.go @@ -17,7 +17,6 @@ const resourceAccessPolicy = "nutanix_access_control_policy.test" func TestAccNutanixAccessControlPolicy_basic(t *testing.T) { name := acctest.RandomWithPrefix("accest-access-policy") description := "Description of my access control policy" - uuidRole := "760dac6c-be97-4b24-adb0-e3c3026dc8d5" nameUpdated := acctest.RandomWithPrefix("accest-access-policy") descriptionUpdated := "Description of my access control policy updated" @@ -27,7 +26,7 @@ func TestAccNutanixAccessControlPolicy_basic(t *testing.T) { CheckDestroy: testAccCheckNutanixAccessControlPolicyDestroy, Steps: []resource.TestStep{ { - Config: testAccNutanixAccessControlPolicyConfig(uuidRole, name, description), + Config: testAccNutanixAccessControlPolicyConfig(name, description), Check: resource.ComposeTestCheckFunc( testAccCheckNutanixAccessControlPolicyExists(), resource.TestCheckResourceAttr(resourceAccessPolicy, "name", name), @@ -35,7 +34,7 @@ func TestAccNutanixAccessControlPolicy_basic(t *testing.T) { ), }, { - Config: testAccNutanixAccessControlPolicyConfig(uuidRole, nameUpdated, descriptionUpdated), + Config: testAccNutanixAccessControlPolicyConfig(nameUpdated, descriptionUpdated), Check: resource.ComposeTestCheckFunc( testAccCheckNutanixAccessControlPolicyExists(), resource.TestCheckResourceAttr(resourceAccessPolicy, "name", nameUpdated), @@ -54,7 +53,6 @@ func TestAccNutanixAccessControlPolicy_basic(t *testing.T) { func TestAccNutanixAccessControlPolicy_WithUser(t *testing.T) { name := acctest.RandomWithPrefix("accest-access-policy") description := "Description of my access control policy" - uuidRole := "760dac6c-be97-4b24-adb0-e3c3026dc8d5" nameUpdated := acctest.RandomWithPrefix("accest-access-policy") descriptionUpdated := "Description of my access control policy updated" @@ -64,7 +62,7 @@ func TestAccNutanixAccessControlPolicy_WithUser(t *testing.T) { CheckDestroy: testAccCheckNutanixAccessControlPolicyDestroy, Steps: []resource.TestStep{ { - Config: testAccNutanixAccessControlPolicyConfigWithUser(uuidRole, name, description), + Config: testAccNutanixAccessControlPolicyConfigWithUser(name, description), Check: resource.ComposeTestCheckFunc( testAccCheckNutanixAccessControlPolicyExists(), resource.TestCheckResourceAttr(resourceAccessPolicy, "name", name), @@ -72,7 +70,7 @@ func TestAccNutanixAccessControlPolicy_WithUser(t *testing.T) { ), }, { - Config: testAccNutanixAccessControlPolicyConfigWithUser(uuidRole, nameUpdated, descriptionUpdated), + Config: testAccNutanixAccessControlPolicyConfigWithUser(nameUpdated, descriptionUpdated), Check: resource.ComposeTestCheckFunc( testAccCheckNutanixAccessControlPolicyExists(), resource.TestCheckResourceAttr(resourceAccessPolicy, "name", nameUpdated), @@ -91,7 +89,6 @@ func TestAccNutanixAccessControlPolicy_WithUser(t *testing.T) { func TestAccNutanixAccessControlPolicy_WithCategory(t *testing.T) { name := acctest.RandomWithPrefix("accest-access-policy") description := "Description of my access control policy" - uuidRole := "760dac6c-be97-4b24-adb0-e3c3026dc8d5" nameUpdated := acctest.RandomWithPrefix("accest-access-policy") descriptionUpdated := "Description of my access control policy updated" @@ -101,7 +98,7 @@ func TestAccNutanixAccessControlPolicy_WithCategory(t *testing.T) { CheckDestroy: testAccCheckNutanixAccessControlPolicyDestroy, Steps: []resource.TestStep{ { - Config: testAccNutanixAccessControlPolicyConfigWithCategory(uuidRole, name, description, "Production"), + Config: testAccNutanixAccessControlPolicyConfigWithCategory(name, description, "Production"), Check: resource.ComposeTestCheckFunc( testAccCheckNutanixAccessControlPolicyExists(), testAccCheckNutanixCategories(resourceAccessPolicy), @@ -113,7 +110,7 @@ func TestAccNutanixAccessControlPolicy_WithCategory(t *testing.T) { ), }, { - Config: testAccNutanixAccessControlPolicyConfigWithCategory(uuidRole, nameUpdated, descriptionUpdated, "Staging"), + Config: testAccNutanixAccessControlPolicyConfigWithCategory(nameUpdated, descriptionUpdated, "Staging"), Check: resource.ComposeTestCheckFunc( testAccCheckNutanixAccessControlPolicyExists(), resource.TestCheckResourceAttr(resourceAccessPolicy, "categories.#", "1"), @@ -168,44 +165,68 @@ func testAccCheckNutanixAccessControlPolicyDestroy(s *terraform.State) error { return nil } -func testAccNutanixAccessControlPolicyConfig(uuidRole, name, description string) string { +func testAccNutanixAccessControlPolicyConfig(name, description string) string { return fmt.Sprintf(` +resource "nutanix_role" "test" { + name = "test role" + description = "description role" + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } +} resource "nutanix_access_control_policy" "test" { name = "%[1]s" description = "%[2]s" role_reference { kind = "role" - uuid = "%[3]s" + uuid = nutanix_role.test.id } } -`, name, description, uuidRole) +`, name, description) } -func testAccNutanixAccessControlPolicyConfigWithCategory(uuidRole, name, description, categoryValue string) string { +func testAccNutanixAccessControlPolicyConfigWithCategory(name, description, categoryValue string) string { return fmt.Sprintf(` +resource "nutanix_role" "test" { + name = "test role" + description = "description role" + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } +} resource "nutanix_access_control_policy" "test" { name = "%[1]s" description = "%[2]s" role_reference { kind = "role" - uuid = "%[3]s" + uuid = nutanix_role.test.id } categories { name = "Environment" - value = "%[4]s" + value = "%[3]s" } } -`, name, description, uuidRole, categoryValue) +`, name, description, categoryValue) } -func testAccNutanixAccessControlPolicyConfigWithUser(uuidRole, name, description string) string { +func testAccNutanixAccessControlPolicyConfigWithUser(name, description string) string { return fmt.Sprintf(` +resource "nutanix_role" "test" { + name = "test role" + description = "description role" + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } +} resource "nutanix_access_control_policy" "test" { name = "%[1]s" description = "%[2]s" role_reference { kind = "role" - uuid = "%[3]s" + uuid = nutanix_role.test.id } user_reference_list{ uuid = "00000000-0000-0000-0000-000000000000" @@ -274,5 +295,5 @@ resource "nutanix_access_control_policy" "test" { } } } -`, name, description, uuidRole) +`, name, description) } diff --git a/nutanix/resource_nutanix_project.go b/nutanix/resource_nutanix_project.go index 50d08a9c2..7c65622e5 100644 --- a/nutanix/resource_nutanix_project.go +++ b/nutanix/resource_nutanix_project.go @@ -141,7 +141,7 @@ func resourceNutanixProject() *schema.Resource { }, }, "user_reference_list": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, Computed: true, Elem: &schema.Resource{ @@ -164,7 +164,7 @@ func resourceNutanixProject() *schema.Resource { }, }, "external_user_group_reference_list": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, Computed: true, Elem: &schema.Resource{ @@ -446,10 +446,10 @@ func resourceNutanixProjectUpdate(d *schema.ResourceData, meta interface{}) erro project.Spec.Resources.DefaultSubnetReference = expandReferenceList(d, "default_subnet_reference")[0] } if d.HasChange("user_reference_list") { - project.Spec.Resources.UserReferenceList = expandReferenceList(d, "user_reference_list") + project.Spec.Resources.UserReferenceList = expandReferenceSet(d, "user_reference_list") } if d.HasChange("external_user_group_reference_list") { - project.Spec.Resources.ExternalUserGroupReferenceList = expandReferenceList(d, "external_user_group_reference_list") + project.Spec.Resources.ExternalUserGroupReferenceList = expandReferenceSet(d, "external_user_group_reference_list") } if d.HasChange("subnet_reference_list") { project.Spec.Resources.SubnetReferenceList = expandReferenceList(d, "subnet_reference_list") @@ -495,8 +495,8 @@ func expandProjectSpec(d *schema.ResourceData) *v3.ProjectSpec { AccountReferenceList: expandReferenceList(d, "account_reference_list"), EnvironmentReferenceList: expandReferenceList(d, "environment_reference_list"), DefaultSubnetReference: expandReferenceList(d, "default_subnet_reference")[0], - UserReferenceList: expandReferenceList(d, "user_reference_list"), - ExternalUserGroupReferenceList: expandReferenceList(d, "external_user_group_reference_list"), + UserReferenceList: expandReferenceSet(d, "user_reference_list"), + ExternalUserGroupReferenceList: expandReferenceSet(d, "external_user_group_reference_list"), SubnetReferenceList: expandReferenceList(d, "subnet_reference_list"), ExternalNetworkList: expandReferenceList(d, "external_network_list"), }, @@ -562,6 +562,16 @@ func expandReferenceList(d *schema.ResourceData, key string) []*v3.ReferenceValu return list } +func expandReferenceSet(d *schema.ResourceData, key string) []*v3.ReferenceValues { + references := d.Get(key).(*schema.Set).List() + list := make([]*v3.ReferenceValues, len(references)) + + for i, r := range references { + list[i] = expandReferenceByMap(cast.ToStringMap(r)) + } + return list +} + func expandMetadata(d *schema.ResourceData, kind string) *v3.Metadata { metadata := new(v3.Metadata) diff --git a/nutanix/resource_nutanix_role.go b/nutanix/resource_nutanix_role.go new file mode 100644 index 000000000..72d6cf44c --- /dev/null +++ b/nutanix/resource_nutanix_role.go @@ -0,0 +1,406 @@ +package nutanix + +import ( + "bytes" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/spf13/cast" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func resourceNutanixRole() *schema.Resource { + return &schema.Resource{ + Create: resourceNutanixRoleCreate, + Read: resourceNutanixRoleRead, + Update: resourceNutanixRoleUpdate, + Delete: resourceNutanixRoleDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "last_update_time": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "spec_version": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "spec_hash": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "categories": categoriesSchema(), + "owner_reference": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "project_reference": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + "kind": { + Type: schema.TypeString, + Optional: true, + Default: "project", + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "permission_reference_list": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + Default: "permission", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + Set: func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["kind"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["uuid"].(string))) + return hashcode.String(buf.String()) + }, + }, + }, + } +} + +func resourceNutanixRoleCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + + request := &v3.Role{} + spec := &v3.RoleSpec{} + metadata := &v3.Metadata{} + role := &v3.RoleResources{} + + name, nameOk := d.GetOk("name") + permissions, permissionsOk := d.GetOk("permission_reference_list") + + if !nameOk && !permissionsOk { + return fmt.Errorf("please provide the required `name` and `permission_reference_list` attribute") + } + + if err := getMetadataAttributesV2(d, metadata, "role"); err != nil { + return err + } + + spec.Name = utils.StringPtr(name.(string)) + if desc, descOk := d.GetOk("description"); descOk { + spec.Description = utils.StringPtr(desc.(string)) + } + role.PermissionReferenceList = validateArrayRef(permissions.(*schema.Set), nil) + + if name, ok := d.GetOk("name"); ok { + spec.Name = utils.StringPtr(name.(string)) + } + spec.Resources = role + request.Metadata = metadata + request.Spec = spec + + resp, err := conn.V3.CreateRole(request) + if err != nil { + return fmt.Errorf("error creating Nutanix Role %s: %+v", utils.StringValue(spec.Name), err) + } + + taskUUID := resp.Status.ExecutionContext.TaskUUID.(string) + + // Wait for the Role to be available + stateConf := &resource.StateChangeConf{ + Pending: []string{"QUEUED", "RUNNING", "PENDING"}, + Target: []string{"SUCCEEDED"}, + Refresh: taskStateRefreshFunc(conn, taskUUID), + Timeout: subnetTimeout, + Delay: subnetDelay, + MinTimeout: subnetMinTimeout, + } + + if _, err := stateConf.WaitForState(); err != nil { + id := d.Id() + d.SetId("") + return fmt.Errorf("error waiting for role id (%s) to create: %+v", id, err) + } + + // Setting Description because in Get request is not present. + d.Set("description", utils.StringValue(resp.Spec.Description)) + + d.SetId(utils.StringValue(resp.Metadata.UUID)) + + return resourceNutanixRoleRead(d, meta) +} + +func resourceNutanixRoleRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + id := d.Id() + resp, err := conn.V3.GetRole(id) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + return nil + } + errDel := resourceNutanixRoleDelete(d, meta) + if errDel != nil { + return fmt.Errorf("error deleting role (%s) after read error: %+v", id, errDel) + } + d.SetId("") + return fmt.Errorf("error reading role id (%s): %+v", id, err) + } + + m, c := setRSEntityMetadata(resp.Metadata) + + if err := d.Set("metadata", m); err != nil { + return err + } + if err := d.Set("categories", c); err != nil { + return err + } + if err := d.Set("project_reference", flattenReferenceValuesList(resp.Metadata.ProjectReference)); err != nil { + return err + } + if err := d.Set("owner_reference", flattenReferenceValuesList(resp.Metadata.OwnerReference)); err != nil { + return err + } + d.Set("api_version", resp.APIVersion) + + if status := resp.Status; status != nil { + if err := d.Set("name", utils.StringValue(resp.Status.Name)); err != nil { + return err + } + if err := d.Set("description", utils.StringValue(resp.Status.Description)); err != nil { + return err + } + if err := d.Set("state", utils.StringValue(resp.Status.State)); err != nil { + return err + } + + if res := status.Resources; res != nil { + if err := d.Set("permission_reference_list", flattenArrayReferenceValues(status.Resources.PermissionReferenceList)); err != nil { + return err + } + } + } + + return nil +} + +func resourceNutanixRoleUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + request := &v3.Role{} + metadata := &v3.Metadata{} + res := &v3.RoleResources{} + spec := &v3.RoleSpec{} + + id := d.Id() + response, err := conn.V3.GetRole(id) + + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return fmt.Errorf("error retrieving for role id (%s) :%+v", id, err) + } + + if response.Metadata != nil { + metadata = response.Metadata + } + + if response.Spec != nil { + spec = response.Spec + + if response.Spec.Resources != nil { + res = response.Spec.Resources + } + } + + if d.HasChange("categories") { + metadata.Categories = expandCategories(d.Get("categories")) + } + if d.HasChange("owner_reference") { + metadata.OwnerReference = validateRefList(d.Get("owner_reference").([]interface{}), nil) + } + if d.HasChange("project_reference") { + metadata.ProjectReference = validateRefList(d.Get("project_reference").([]interface{}), utils.StringPtr("project")) + } + if d.HasChange("name") { + spec.Name = utils.StringPtr(d.Get("name").(string)) + } + if d.HasChange("description") { + spec.Description = utils.StringPtr(d.Get("description").(string)) + } + + if d.HasChange("permission_reference_list") { + res.PermissionReferenceList = validateArrayRef(d.Get("permission_reference_list"), nil) + } + + spec.Resources = res + request.Metadata = metadata + request.Spec = spec + + resp, errUpdate := conn.V3.UpdateRole(d.Id(), request) + if errUpdate != nil { + return fmt.Errorf("error updating role id %s): %s", d.Id(), errUpdate) + } + + taskUUID := resp.Status.ExecutionContext.TaskUUID.(string) + + // Wait for the VM to be available + stateConf := &resource.StateChangeConf{ + Pending: []string{"QUEUED", "RUNNING"}, + Target: []string{"SUCCEEDED"}, + Refresh: taskStateRefreshFunc(conn, taskUUID), + Timeout: subnetTimeout, + Delay: subnetDelay, + MinTimeout: subnetMinTimeout, + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf( + "error waiting for role (%s) to update: %s", d.Id(), err) + } + // Setting Description because in Get request is not present. + d.Set("description", utils.StringValue(resp.Spec.Description)) + + return resourceNutanixRoleRead(d, meta) +} + +func resourceNutanixRoleDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + + resp, err := conn.V3.DeleteRole(d.Id()) + if err != nil { + return fmt.Errorf("error deleting role id %s): %s", d.Id(), err) + } + + // Wait for the VM to be available + stateConf := &resource.StateChangeConf{ + Pending: []string{"QUEUED", "RUNNING", "DELETED_PENDING"}, + Target: []string{"SUCCEEDED"}, + Refresh: taskStateRefreshFunc(conn, cast.ToString(resp.Status.ExecutionContext.TaskUUID)), + Timeout: subnetTimeout, + Delay: subnetDelay, + MinTimeout: subnetMinTimeout, + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf( + "error waiting for role (%s) to update: %s", d.Id(), err) + } + + d.SetId("") + return nil +} + +func resourceNutanixRoleExists(conn *v3.Client, name string) (*string, error) { + var accessUUID *string + + filter := fmt.Sprintf("name==%s", name) + accessList, err := conn.V3.ListAllRole(filter) + + if err != nil { + return nil, err + } + + for _, access := range accessList.Entities { + if utils.StringValue(access.Status.Name) == name { + accessUUID = access.Metadata.UUID + } + } + return accessUUID, nil +} diff --git a/nutanix/resource_nutanix_role_test.go b/nutanix/resource_nutanix_role_test.go new file mode 100644 index 000000000..c5eebca98 --- /dev/null +++ b/nutanix/resource_nutanix_role_test.go @@ -0,0 +1,240 @@ +package nutanix + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +const resourceRole = "nutanix_role.test" + +func TestAccNutanixRole_basic(t *testing.T) { + name := acctest.RandomWithPrefix("accest-role") + description := "Description of my role" + nameUpdated := acctest.RandomWithPrefix("accest-role") + descriptionUpdated := "Description of my role updated" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNutanixRoleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNutanixRoleConfig(name, description), + Check: resource.ComposeTestCheckFunc( + testAccCheckNutanixRoleExists(), + resource.TestCheckResourceAttr(resourceRole, "name", name), + resource.TestCheckResourceAttr(resourceRole, "description", description), + ), + }, + { + Config: testAccNutanixRoleConfig(nameUpdated, descriptionUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckNutanixRoleExists(), + resource.TestCheckResourceAttr(resourceRole, "name", nameUpdated), + resource.TestCheckResourceAttr(resourceRole, "description", descriptionUpdated), + ), + }, + { + ResourceName: resourceRole, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNutanixRole_WithCategory(t *testing.T) { + name := acctest.RandomWithPrefix("accest-role") + description := "Description of my role" + nameUpdated := acctest.RandomWithPrefix("accest-role") + descriptionUpdated := "Description of my role updated" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNutanixRoleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNutanixRoleConfigWithCategory(name, description, "Production"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNutanixRoleExists(), + testAccCheckNutanixCategories(resourceRole), + resource.TestCheckResourceAttr(resourceRole, "categories.#", "1"), + resource.TestCheckResourceAttrSet(resourceRole, "categories.2228745532.name"), + resource.TestCheckResourceAttrSet(resourceRole, "categories.2228745532.value"), + resource.TestCheckResourceAttr(resourceRole, "categories.2228745532.name", "Environment"), + resource.TestCheckResourceAttr(resourceRole, "categories.2228745532.value", "Production"), + ), + }, + { + Config: testAccNutanixRoleConfigWithCategory(nameUpdated, descriptionUpdated, "Staging"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNutanixRoleExists(), + resource.TestCheckResourceAttr(resourceRole, "categories.#", "1"), + resource.TestCheckResourceAttrSet(resourceRole, "categories.2940305446.name"), + resource.TestCheckResourceAttrSet(resourceRole, "categories.2940305446.value"), + resource.TestCheckResourceAttr(resourceRole, "categories.2940305446.name", "Environment"), + resource.TestCheckResourceAttr(resourceRole, "categories.2940305446.value", "Staging"), + ), + }, + { + ResourceName: resourceRole, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckNutanixRoleExists() resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceRole] + if !ok { + return fmt.Errorf("not found: %s", resourceRole) + } + + pretty, _ := json.MarshalIndent(rs, "", " ") + fmt.Print("\n\n[DEBUG] State of Role", string(pretty)) + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + return nil + } +} + +func testAccCheckNutanixRoleDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "nutanix_role" { + continue + } + if _, err := resourceNutanixRoleExists(conn.API, rs.Primary.ID); err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + return nil + } + return err + } + } + + return nil +} + +func testAccNutanixRoleConfig(name, description string) string { + return fmt.Sprintf(` +resource "nutanix_role" "test" { + name = "%[1]s" + description = "%[2]s" + permission_reference_list { + kind = "permission" + uuid = "d08ea95c-8221-4590-a77a-52d69639959a" + } + permission_reference_list { + kind = "permission" + uuid = "1a8a65c0-4333-42c6-9039-fd2585ceead7" + } + permission_reference_list { + kind = "permission" + uuid = "bea75573-e8fe-42a3-817a-bd1bd98ab110" + } + permission_reference_list { + kind = "permission" + uuid = "93e1cc93-d799-4f44-84ad-534814f6db0d" + } + permission_reference_list { + kind = "permission" + uuid = "62f53a1a-324c-4da6-bcb8-2cecc07b2cb7" + } + permission_reference_list { + kind = "permission" + uuid = "62f53a1a-324c-4da6-bcb8-2cecc07b2cb7" + } + permission_reference_list { + kind = "permission" + uuid = "ef38a553-a20f-4a2b-b12d-bb9cca03cbdd" + } + permission_reference_list { + kind = "permission" + uuid = "6e768a07-21ef-4615-84d0-7ec442ec942f" + } + permission_reference_list { + kind = "permission" + uuid = "91b77724-b163-473f-94a8-d016e75c18bd" + } + permission_reference_list { + kind = "permission" + uuid = "491ae1d0-5a8f-4bcc-9cee-068cd01c9274" + } + permission_reference_list { + kind = "permission" + uuid = "1dbfb7b4-9896-4c2a-b6fe-fbf113bae306" + } + permission_reference_list { + kind = "permission" + uuid = "740d29f7-18ae-4d07-aeef-3fc901c1887a" + } + permission_reference_list { + kind = "permission" + uuid = "b28f35be-6561-4a4a-9d90-a298d2de33d7" + } + permission_reference_list { + kind = "permission" + uuid = "428fad6c-8735-4a7d-bad3-8497bef051c8" + } + permission_reference_list { + kind = "permission" + uuid = "ea445ec5-f9bb-4af6-92e8-0d72d11ada85" + } + permission_reference_list { + kind = "permission" + uuid = "85a24ad8-67b6-4b63-b30f-96da1baca161" + } + permission_reference_list { + kind = "permission" + uuid = "d370823b-82d8-4518-a486-b75ba8e130d6" + } + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } + permission_reference_list { + kind = "permission" + uuid = "4e8e9007-8fbe-4709-a069-278259238e55" + } + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } +} +`, name, description) +} + +func testAccNutanixRoleConfigWithCategory(name, description, categoryValue string) string { + return fmt.Sprintf(` +resource "nutanix_role" "test" { + name = "%[1]s" + description = "%[2]s" + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } + categories { + name = "Environment" + value = "%[3]s" + } +} +`, name, description, categoryValue) +} diff --git a/nutanix/resource_nutanix_user.go b/nutanix/resource_nutanix_user.go new file mode 100644 index 000000000..6a2286b71 --- /dev/null +++ b/nutanix/resource_nutanix_user.go @@ -0,0 +1,604 @@ +package nutanix + +import ( + "fmt" + "log" + "reflect" + "strings" + "time" + + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +const ( + // UserKind Represents kind of resource + UserKind = "user" +) + +var ( + userDelay = 10 * time.Second + userMinTimeout = 3 * time.Second +) + +func resourceNutanixUser() *schema.Resource { + return &schema.Resource{ + Create: resourceNutanixUserCreate, + Read: resourceNutanixUserRead, + Update: resourceNutanixUserUpdate, + Delete: resourceNutanixUserDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "last_update_time": { + Type: schema.TypeString, + Computed: true, + }, + + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "spec_version": { + Type: schema.TypeString, + Computed: true, + }, + "spec_hash": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "categories": categoriesSchema(), + "owner_reference": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "project_reference": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "directory_service_user": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_principal_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + //ValidateFunc: validation.StringInSlice([]string{"role"}, false), + }, + "directory_service_reference": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + Default: "directory_service", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "default_user_principal_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "identity_provider_user": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Optional: true, + Computed: true, + //ValidateFunc: validation.StringInSlice([]string{"role"}, false), + }, + "identity_provider_reference": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + Default: "identity_provider", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "user_type": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "project_reference_list": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "access_control_policy_reference_list": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceNutanixUserCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Creating User: %s", d.Get("name").(string)) + client := meta.(*Client) + conn := client.API + timeout := client.WaitTimeout + + if client.WaitTimeout == 0 { + timeout = 10 + } + + request := &v3.UserIntentInput{} + + metadata := &v3.Metadata{} + + if err := getMetadataAttributes(d, metadata, "user"); err != nil { + return err + } + + spec := &v3.UserSpec{ + Resources: &v3.UserResources{ + DirectoryServiceUser: expandDirectoryServiceUser(d), + IdentityProviderUser: expandIdentityProviderUser(d), + }, + } + + request.Metadata = metadata + request.Spec = spec + + // Make request to the API + resp, err := conn.V3.CreateUser(request) + if err != nil { + return fmt.Errorf("error creating Nutanix User: %+v", err) + } + + UUID := *resp.Metadata.UUID + // set terraform state + d.SetId(UUID) + + taskUUID := resp.Status.ExecutionContext.TaskUUID.(string) + + // Wait for the Image to be available + stateConf := &resource.StateChangeConf{ + Pending: []string{"QUEUED", "RUNNING"}, + Target: []string{"SUCCEEDED"}, + Refresh: taskStateRefreshFunc(conn, taskUUID), + Timeout: time.Duration(timeout) * time.Minute, + Delay: userDelay, + MinTimeout: userMinTimeout, + } + + if _, errw := stateConf.WaitForState(); errw != nil { + // delErr := resourceNutanixUserDelete(d, meta) + // if delErr != nil { + // return fmt.Errorf("error waiting for image (%s) to delete in creation: %s", d.Id(), delErr) + // } + d.SetId("") + return fmt.Errorf("error waiting for user (%s) to create: %s", UUID, errw) + } + return resourceNutanixUserRead(d, meta) +} + +func resourceNutanixUserRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Reading User: %s", d.Id()) + + // Get client connection + conn := meta.(*Client).API + uuid := d.Id() + + // Make request to the API + resp, err := conn.V3.GetUser(uuid) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + return nil + } + return fmt.Errorf("error reading user UUID (%s) with error %s", uuid, err) + } + + m, c := setRSEntityMetadata(resp.Metadata) + + if err = d.Set("metadata", m); err != nil { + return fmt.Errorf("error setting metadata for user UUID(%s), %s", d.Id(), err) + } + if err = d.Set("categories", c); err != nil { + return fmt.Errorf("error setting categories for user UUID(%s), %s", d.Id(), err) + } + + if err = d.Set("owner_reference", flattenReferenceValues(resp.Metadata.OwnerReference)); err != nil { + return fmt.Errorf("error setting owner_reference for user UUID(%s), %s", d.Id(), err) + } + d.Set("api_version", utils.StringValue(resp.APIVersion)) + d.Set("name", utils.StringValue(resp.Status.Name)) + + if err = d.Set("state", resp.Status.State); err != nil { + return fmt.Errorf("error setting state for user UUID(%s), %s", d.Id(), err) + } + + if err = d.Set("directory_service_user", flattenDirectoryServiceUser(resp.Status.Resources.DirectoryServiceUser)); err != nil { + return fmt.Errorf("error setting directory_service_user for user UUID(%s), %s", d.Id(), err) + } + + //TODO: change to status when API is fixed + if err = d.Set("identity_provider_user", flattenIdentityProviderUser(resp.Spec.Resources.IdentityProviderUser)); err != nil { + return fmt.Errorf("error setting identity_provider_user for user UUID(%s), %s", d.Id(), err) + } + + if err = d.Set("user_type", resp.Status.Resources.UserType); err != nil { + return fmt.Errorf("error setting user_type for user UUID(%s), %s", d.Id(), err) + } + + if err = d.Set("display_name", resp.Status.Resources.DisplayName); err != nil { + return fmt.Errorf("error setting display_name for user UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("project_reference_list", flattenArrayReferenceValues(resp.Status.Resources.ProjectsReferenceList)); err != nil { + return fmt.Errorf("error setting project_reference_list for user UUID(%s), %s", d.Id(), err) + } + + if err := d.Set("access_control_policy_reference_list", flattenArrayReferenceValues(resp.Status.Resources.AccessControlPolicyReferenceList)); err != nil { + return fmt.Errorf("error setting access_control_policy_reference_list for user UUID(%s), %s", d.Id(), err) + } + + //TODO: + //identity_provider_user + + return nil +} + +func resourceNutanixUserUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Client) + conn := client.API + timeout := client.WaitTimeout + + if client.WaitTimeout == 0 { + timeout = 10 + } + + // get state + request := &v3.UserIntentInput{} + metadata := &v3.Metadata{} + spec := &v3.UserSpec{} + res := &v3.UserResources{} + + response, err := conn.V3.GetUser(d.Id()) + + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return err + } + + if response.Metadata != nil { + metadata = response.Metadata + } + + if response.Spec != nil { + spec = response.Spec + + if response.Spec.Resources != nil { + res = response.Spec.Resources + } + } + + if d.HasChange("categories") { + metadata.Categories = expandCategories(d.Get("categories")) + } + + if d.HasChange("owner_reference") { + or := d.Get("owner_reference").(map[string]interface{}) + metadata.OwnerReference = validateRef(or) + } + + if d.HasChange("project_reference") { + pr := d.Get("project_reference").(map[string]interface{}) + metadata.ProjectReference = validateRef(pr) + } + + if d.HasChange("directory_service_user") { + res.DirectoryServiceUser = expandDirectoryServiceUser(d) + } + + if d.HasChange("identity_provider_user") { + res.IdentityProviderUser = expandIdentityProviderUser(d) + } + + request.Metadata = metadata + request.Spec = spec + + resp, errUpdate := conn.V3.UpdateUser(d.Id(), request) + + if errUpdate != nil { + return fmt.Errorf("error updating user(%s) %s", d.Id(), errUpdate) + } + + taskUUID := resp.Status.ExecutionContext.TaskUUID.(string) + + // Wait for the Image to be available + stateConf := &resource.StateChangeConf{ + Pending: []string{"QUEUED", "RUNNING"}, + Target: []string{"SUCCEEDED"}, + Refresh: taskStateRefreshFunc(conn, taskUUID), + Timeout: time.Duration(timeout) * time.Minute, + Delay: userDelay, + MinTimeout: userMinTimeout, + } + + if _, err := stateConf.WaitForState(); err != nil { + // delErr := resourceNutanixUserDelete(d, meta) + // if delErr != nil { + // return fmt.Errorf("error waiting for image (%s) to delete in update: %s", d.Id(), delErr) + // } + uuid := d.Id() + d.SetId("") + return fmt.Errorf("error waiting for user (%s) to update: %s", uuid, err) + } + + return resourceNutanixUserRead(d, meta) +} + +func resourceNutanixUserDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Deleting User: %s", d.Get("display_name").(string)) + + client := meta.(*Client) + conn := client.API + timeout := client.WaitTimeout + + if client.WaitTimeout == 0 { + timeout = 10 + } + + UUID := d.Id() + + resp, err := conn.V3.DeleteUser(UUID) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return err + } + + taskUUID := resp.Status.ExecutionContext.TaskUUID.(string) + + // Wait for the Image to be available + stateConf := &resource.StateChangeConf{ + Pending: []string{"QUEUED", "RUNNING"}, + Target: []string{"SUCCEEDED"}, + Refresh: taskStateRefreshFunc(conn, taskUUID), + Timeout: time.Duration(timeout) * time.Minute, + Delay: userDelay, + MinTimeout: userMinTimeout, + } + + if _, err := stateConf.WaitForState(); err != nil { + d.SetId("") + return fmt.Errorf("error waiting for user (%s) to delete: %s", d.Id(), err) + } + + log.Printf("[DEBUG] USER DELETED") + + d.SetId("") + return nil +} + +func expandDirectoryServiceUser(d *schema.ResourceData) *v3.DirectoryServiceUser { + directoryServiceUserState, ok := d.GetOk("directory_service_user") + if !ok { + return nil + } + + directoryServiceUserMap := directoryServiceUserState.([]interface{})[0].(map[string]interface{}) + directoryServiceUser := &v3.DirectoryServiceUser{} + + if upn, ok := directoryServiceUserMap["user_principal_name"]; ok { + directoryServiceUser.UserPrincipalName = utils.StringPtr(upn.(string)) + } + + if dpr, ok := directoryServiceUserMap["directory_service_reference"]; ok { + directoryServiceUser.DirectoryServiceReference = expandReference(dpr.([]interface{})[0].(map[string]interface{})) + } + + if !reflect.DeepEqual(*directoryServiceUser, v3.DirectoryServiceUser{}) { + return directoryServiceUser + } + return nil +} + +func expandIdentityProviderUser(d *schema.ResourceData) *v3.IdentityProvider { + identityProviderState, ok := d.GetOk("identity_provider_user") + if !ok { + return nil + } + + identityProviderMap := identityProviderState.([]interface{})[0].(map[string]interface{}) + identityProvider := &v3.IdentityProvider{} + + if username, ok := identityProviderMap["username"]; ok { + identityProvider.Username = utils.StringPtr(username.(string)) + } + + if ipr, ok := identityProviderMap["identity_provider_reference"]; ok { + identityProvider.IdentityProviderReference = expandReference(ipr.([]interface{})[0].(map[string]interface{})) + } + + if !reflect.DeepEqual(*identityProvider, v3.IdentityProvider{}) { + return identityProvider + } + return nil +} + +func flattenDirectoryServiceUser(dsu *v3.DirectoryServiceUser) []interface{} { + log.Print("in flattenDirectoryServiceUser") + if dsu != nil { + directoryServiceUserMap := map[string]interface{}{} + + if dsu.DefaultUserPrincipalName != nil { + directoryServiceUserMap["default_user_principal_name"] = dsu.DefaultUserPrincipalName + } + + if dsu.UserPrincipalName != nil { + directoryServiceUserMap["user_principal_name"] = dsu.UserPrincipalName + } + + if dsu.DirectoryServiceReference != nil { + directoryServiceUserMap["directory_service_reference"] = []interface{}{flattenReferenceValues(dsu.DirectoryServiceReference)} + } + return []interface{}{directoryServiceUserMap} + } + return nil +} + +func flattenIdentityProviderUser(ipu *v3.IdentityProvider) []interface{} { + if ipu != nil { + identityProviderUserMap := map[string]interface{}{} + + if ipu.Username != nil { + identityProviderUserMap["username"] = ipu.Username + } + + if ipu.IdentityProviderReference != nil { + identityProviderUserMap["identity_provider_reference"] = []interface{}{flattenReferenceValues(ipu.IdentityProviderReference)} + } + + return []interface{}{identityProviderUserMap} + } + return nil +} diff --git a/nutanix/resource_nutanix_user_test.go b/nutanix/resource_nutanix_user_test.go new file mode 100644 index 000000000..8874e20bd --- /dev/null +++ b/nutanix/resource_nutanix_user_test.go @@ -0,0 +1,129 @@ +package nutanix + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +const resourceNameUser = "nutanix_user.user" + +func TestAccNutanixUser_basic(t *testing.T) { + principalName := "dou-user@ntnxlab.local" + directoryServiceUUID := "dd19a896-8e72-4158-b716-98455ceda220" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNutanixUserDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNutanixUserConfig(principalName, directoryServiceUUID), + Check: resource.ComposeTestCheckFunc( + testAccCheckNutanixUserExists(resourceNameUser), + resource.TestCheckResourceAttr(resourceNameUser, "name", principalName), + resource.TestCheckResourceAttr(resourceNameUser, "directory_service_user.#", "1"), + ), + }, + { + Config: testAccNutanixUserConfig(principalName, directoryServiceUUID), + Check: resource.ComposeTestCheckFunc( + testAccCheckNutanixUserExists(resourceNameUser), + resource.TestCheckResourceAttr(resourceNameUser, "name", principalName), + resource.TestCheckResourceAttr(resourceNameUser, "directory_service_user.#", "1"), + ), + }, + { + ResourceName: resourceNameUser, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNutanixUser_IdentityProvider(t *testing.T) { + username := "dou-user-2@ntnxlab.local" + identityProviderUUID := "02316a2c-cc8c-41de-9abb-f07c4da58fda" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNutanixUserDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNutanixUserConfigIdentityProvider(username, identityProviderUUID), + Check: resource.ComposeTestCheckFunc( + testAccCheckNutanixUserExists(resourceNameUser), + resource.TestCheckResourceAttr(resourceNameUser, "name", username), + resource.TestCheckResourceAttr(resourceNameUser, "identity_provider_user.#", "1"), + ), + }, + { + ResourceName: resourceNameUser, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckNutanixUserDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "nutanix_user" { + continue + } + if _, err := conn.API.V3.GetUser(rs.Primary.ID); err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + return nil + } + return err + } + } + + return nil +} + +func testAccCheckNutanixUserExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + return nil + } +} + +func testAccNutanixUserConfig(pn, dsuuid string) string { + return fmt.Sprintf(` +resource "nutanix_user" "user" { + directory_service_user { + user_principal_name = "%s" + directory_service_reference { + uuid = "%s" + } + } +} +`, pn, dsuuid) +} + +func testAccNutanixUserConfigIdentityProvider(username, ipuuid string) string { + return fmt.Sprintf(` +resource "nutanix_user" "user" { + identity_provider_user { + username = "%s" + identity_provider_reference { + uuid = "%s" + } + } +} +`, username, ipuuid) +} diff --git a/website/docs/d/role.html.markdown b/website/docs/d/role.html.markdown new file mode 100644 index 000000000..7749617cc --- /dev/null +++ b/website/docs/d/role.html.markdown @@ -0,0 +1,85 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_role" +sidebar_current: "docs-nutanix-resource-role" +description: |- +Describes a Role +--- + +# nutanix_role + +Describes a Role. + +## Example Usage + +``` hcl +resource "nutanix_role" "test" { + name = "NAME" + description = "DESCRIPTION" + permission_reference_list { + kind = "permission" + uuid = "ID OF PERMISSION" + } + + data "nutanix_role" "test" { + role_id = nutanix_role.test.id + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `role_id`: - (Required) The UUID of a Role. + +## Attribute Reference + +The following attributes are exported: + +* `name`: - Name of the Role. +* `description`: - The description of the association of a role to a user in a given context. +* `categories`: - Categories for the Role. +* `project_reference`: - The reference to a project. +* `owner_reference`: - The reference to a user. +* `project_reference`: - The reference to a project. +* `permission_reference_list`: - (Required) List of permission references. + +## Attributes Reference + +The following attributes are exported: + +* `metadata`: - The vm kind metadata. +* `api_version` - The version of the API. +* `state`: - The state of the vm. + +### Metadata + +The metadata attribute exports the following: + +* `last_update_time`: - UTC date and time in RFC-3339 format when vm was last updated. +* `uuid`: - vm UUID. +* `creation_time`: - UTC date and time in RFC-3339 format when vm was created. +* `spec_version`: - Version number of the latest spec. +* `spec_hash`: - Hash of the spec. This will be returned from server. +* `name`: - vm name. +* `should_force_translate`: - Applied on Prism Central only. Indicate whether force to translate the spec of the fanout request to fit the target cluster API schema. + +### Categories + +The categories attribute supports the following: + +* `name`: - the key name. +* `value`: - value of the key. + +### Reference + +The `project_reference`, `owner_reference` attributes supports the following: + +* `kind`: - The kind name (Default value: project)(Required). +* `name`: - the name(Optional). +* `uuid`: - the UUID(Required). + +For `permission_reference_list` are the same as reference but used as array. + +See detailed information in [Nutanix Roles](https://www.nutanix.dev/reference/prism_central/v3/api/roles/). diff --git a/website/docs/d/roles.html.markdown b/website/docs/d/roles.html.markdown new file mode 100644 index 000000000..5eeb7c906 --- /dev/null +++ b/website/docs/d/roles.html.markdown @@ -0,0 +1,75 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_roles" +sidebar_current: "docs-nutanix-resource-roles" +description: |- + Describes a list of roles +--- + +# nutanix_roles + +Describes a list of roles. + +## Example Usage + +``` hcl +data "nutanix_roles" "test" {}} +``` + +## Attribute Reference + +The following attributes are exported: + +* `api_version`: version of the API +* `entities`: List of Projects + +# Entities + +The entities attribute element contains the followings attributes: + +* `name`: - Name of the Access Control Policy. +* `description`: - The description of the association of a role to a user in a given context. +* `categories`: - Categories for the Access Control Policy. +* `project_reference`: - The reference to a project. +* `owner_reference`: - The reference to a user. +* `project_reference`: - The reference to a project. +* `permission_reference_list`: - (Required) List of permission references. + +## Attributes Reference + +The following attributes are exported: + +* `metadata`: - The vm kind metadata. +* `api_version` - The version of the API. +* `state`: - The state of the vm. + +### Metadata + +The metadata attribute exports the following: + +* `last_update_time`: - UTC date and time in RFC-3339 format when vm was last updated. +* `uuid`: - vm UUID. +* `creation_time`: - UTC date and time in RFC-3339 format when vm was created. +* `spec_version`: - Version number of the latest spec. +* `spec_hash`: - Hash of the spec. This will be returned from server. +* `name`: - vm name. +* `should_force_translate`: - Applied on Prism Central only. Indicate whether force to translate the spec of the fanout request to fit the target cluster API schema. + +### Categories + +The categories attribute supports the following: + +* `name`: - the key name. +* `value`: - value of the key. + +### Reference + +The `project_reference`, `owner_reference` attributes supports the following: + +* `kind`: - The kind name (Default value: project)(Required). +* `name`: - the name(Optional). +* `uuid`: - the UUID(Required). + +For `permission_reference_list` are the same as reference but used as array. + +See detailed information in [Nutanix Roles](https://www.nutanix.dev/reference/prism_central/v3/api/roles/). diff --git a/website/docs/d/user.html.markdown b/website/docs/d/user.html.markdown new file mode 100644 index 000000000..3534352a7 --- /dev/null +++ b/website/docs/d/user.html.markdown @@ -0,0 +1,108 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_user" +sidebar_current: "docs-nutanix-datasource-user" +description: |- + This operation retrieves a user based on the input parameters. +--- + +# nutanix_user + +Provides a datasource to retrieve a user based on the input parameters. + +## Example Usage + +``` hcl +resource "nutanix_user" "user" { + directory_service_user { + user_principal_name = "test-user@ntnxlab.local" + directory_service_reference { + uuid = "" + } + } +} + +//Retrieve by UUID +data "nutanix_user" "user" { + uuid = nutanix_user.user.id +} + +//Retrieve by Name +data "nutanix_user" "userbyname" { + name = nutanix_user.user.name +} +``` + + + + +## Argument Reference + +The following arguments are supported: + +* `uuid`: - (Optional) The UUID for the user. +* `name`: - (Optional) The name for the user + + +## Attributes Reference + +The following attributes are exported: + +* `metadata`: - The vm kind metadata. +* `api_version` - The version of the API. +* `state`: - The state of the entity. +* `name`: - The name of the user. +* `user_type`: - The name of the user. +* `display_name`: - The display name of the user (common name) provided by the directory service. +* `project_reference_list`: - A list of projects the user is part of. See #reference for more details. +* `access_control_policy_reference_list`: - List of ACP references. See #reference for more details. +* `directory_service_user`: - (Optional) The directory service user configuration. See below for more information. +* `identity_provider_user`: - (Optional) (Optional) The identity provider user configuration. See below for more information. +* `categories`: - (Optional) Categories for the Access Control Policy. +* `project_reference`: - (Optional) The reference to a project. +* `owner_reference`: - (Optional) The reference to a user. + +### Metadata + +The metadata attribute exports the following: + +* `last_update_time`: - UTC date and time in RFC-3339 format when vm was last updated. +* `uuid`: - vm UUID. +* `creation_time`: - UTC date and time in RFC-3339 format when vm was created. +* `spec_version`: - Version number of the latest spec. +* `spec_hash`: - Hash of the spec. This will be returned from server. +* `name`: - vm name. +* `should_force_translate`: - Applied on Prism Central only. Indicate whether force to translate the spec of the fanout request to fit the target cluster API schema. + +### Categories + +The categories attribute supports the following: + +* `name`: - the key name. +* `value`: - value of the key. + +### Directory Service User + +The directory_service_user argument supports the following: + +* `user_principal_name`: - (Optional) The UserPrincipalName of the user from the directory service. +* `directory_service_reference`: - (Optional) The reference to a directory service. See #reference for to look the supported attributes. + +### Identity Provider User + +The identity_provider_user argument supports the following: + +* `username`: - (Optional) The username from identity provider. Name ID for SAML Identity Provider. +* `identity_provider_reference`: - (Optional) The reference to a identity provider. See #reference for to look the supported attributes. + +### Reference + +The `project_reference`, `owner_reference`, `role_reference` `directory_service_reference` attributes supports the following: + +* `kind`: - The kind name. (Default depends on the resource you are referencing) +* `name`: - the name(Optional). +* `uuid`: - the UUID(Required). + +For `access_control_policy_reference_list` and `project_reference_list` are the same as reference but used as list. + +See detailed information in [Nutanix Users](https://www.nutanix.dev/reference/prism_central/v3/api/users/). diff --git a/website/docs/d/user_group.html.markdown b/website/docs/d/user_group.html.markdown new file mode 100644 index 000000000..3ceafad68 --- /dev/null +++ b/website/docs/d/user_group.html.markdown @@ -0,0 +1,97 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_user_group" +sidebar_current: "docs-nutanix-datasource-user-group" +description: |- + This operation retrieves a user based on the input parameters. +--- + +# nutanix_user_group + +Provides a datasource to retrieve a user group based on the input parameters. + +## Example Usage + +``` hcl + +//Retrieve by UUID +data "nutanix_user_group" "usergroup" { + user_group_id = "dd30a856-8e72-4158-b716-98455ceda220" +} + +//Retrieve by Name +data "nutanix_user_group" "usergroupbyname" { + user_group_name = "example-group-1" +} + +//Retrieve by Distinguished Name +data "nutanix_user_group" "test" { + user_group_distinguished_name = "cn=example-group-1,cn=users,dc=ntnxlab,dc=local" +} +``` + + + + +## Argument Reference + +The following arguments are supported: + +* `user_group_id`: - (Optional) The UUID for the user group +* `user_group_name`: - (Optional) The name for the user group +* `user_group_distinguished_name` - (Optional) The distinguished name for the user group + +## Attributes Reference + +The following attributes are exported: + +* `api_version` - The version of the API. +* `metadata`: - The vm kind metadata. +* `categories`: - The Distinguished Categories for the Access Control Policy. +* `owner_reference`: - The Distinguished The reference to a user. +* `project_reference`: - The Distinguished The reference to a project. +* `user_group_type`: - The type of the user group. +* `display_name`: - The display name of the user group. +* `directory_service_user_group`: - A Directory Service User Group. +* `project_reference_list`: - A list of projects the user is part of. See #reference for more details. +* `access_control_policy_reference_list`: - List of ACP references. See #reference for more details. +* `state`: - The state of the entity. + +### Metadata + +The metadata attribute exports the following: + +* `last_update_time`: - UTC date and time in RFC-3339 format when vm was last updated. +* `uuid`: - vm UUID. +* `creation_time`: - UTC date and time in RFC-3339 format when vm was created. +* `spec_version`: - Version number of the latest spec. +* `spec_hash`: - Hash of the spec. This will be returned from server. +* `name`: - vm name. +* `should_force_translate`: - Applied on Prism Central only. Indicate whether force to translate the spec of the fanout request to fit the target cluster API schema. + +### Categories + +The categories attribute supports the following: + +* `name`: - the key name. +* `value`: - value of the key. + +### Directory Service User Group + +The directory_service_user argument supports the following: + +* `distinguished_name`: - The Distinguished name for the user group +* `directory_service_reference`: - The reference to a directory service. See #reference for to look the supported attributes. + + +### Reference + +The `project_reference`, `owner_reference`, `role_reference` `directory_service_reference` attributes supports the following: + +* `kind`: - The kind name. (Default depends on the resource you are referencing) +* `name`: - the name(Optional). +* `uuid`: - the UUID(Required). + +For `access_control_policy_reference_list` and `project_reference_list` are the same as reference but used as list. + +See detailed information in [Nutanix Users](https://www.nutanix.dev/reference/prism_central/v3/api/user-groups/). diff --git a/website/docs/d/user_groups.html.markdown b/website/docs/d/user_groups.html.markdown new file mode 100644 index 000000000..e919dcd33 --- /dev/null +++ b/website/docs/d/user_groups.html.markdown @@ -0,0 +1,81 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_user_groups" +sidebar_current: "docs-nutanix-datasource-user-groups" +description: |- + Provides a datasource to retrieve all the user groups. +--- + +# nutanix_user_groups + +Provides a datasource to retrieve all the user groups. + +## Example Usage + +``` hcl +data "nutanix_user_groups" "usergroups" {} +``` + + + + +## Argument Reference + +The following attributes are exported: + +# Entities + +The entities attribute element contains the following attributes: + +The following attributes are exported: + +* `api_version` - The version of the API. +* `metadata`: - The vm kind metadata. +* `categories`: - The Distinguished Categories for the Access Control Policy. +* `owner_reference`: - The Distinguished The reference to a user. +* `project_reference`: - The Distinguished The reference to a project. +* `user_group_type`: - The type of the user group. +* `display_name`: - The display name of the user group. +* `directory_service_user_group`: - A Directory Service User Group. +* `project_reference_list`: - A list of projects the user is part of. See #reference for more details. +* `access_control_policy_reference_list`: - List of ACP references. See #reference for more details. +* `state`: - The state of the entity. + +### Metadata + +The metadata attribute exports the following: + +* `last_update_time`: - UTC date and time in RFC-3339 format when vm was last updated. +* `uuid`: - vm UUID. +* `creation_time`: - UTC date and time in RFC-3339 format when vm was created. +* `spec_version`: - Version number of the latest spec. +* `spec_hash`: - Hash of the spec. This will be returned from server. +* `name`: - vm name. +* `should_force_translate`: - Applied on Prism Central only. Indicate whether force to translate the spec of the fanout request to fit the target cluster API schema. + +### Categories + +The categories attribute supports the following: + +* `name`: - the key name. +* `value`: - value of the key. + +### Directory Service User Group + +The directory_service_user argument supports the following: + +* `distinguished_name`: - The Distinguished name for the user group +* `directory_service_reference`: - The reference to a directory service. See #reference for to look the supported attributes. + + +### Reference + +The `project_reference`, `owner_reference`, `role_reference` `directory_service_reference` attributes supports the following: + +* `kind`: - The kind name. (Default depends on the resource you are referencing) +* `name`: - the name(Optional). +* `uuid`: - the UUID(Required). + +For `access_control_policy_reference_list` and `project_reference_list` are the same as reference but used as list. + +See detailed information in [Nutanix Users](https://www.nutanix.dev/reference/prism_central/v3/api/user-groups/). diff --git a/website/docs/d/users.html.markdown b/website/docs/d/users.html.markdown new file mode 100644 index 000000000..d4e12ed44 --- /dev/null +++ b/website/docs/d/users.html.markdown @@ -0,0 +1,94 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_users" +sidebar_current: "docs-nutanix-datasource-users" +description: |- + This operation retrieves a list of all the users. +--- + +# nutanix_user + +Provides a datasource to retrieve all the users. + +## Example Usage + +``` hcl +resource "nutanix_user" "user" { + directory_service_user { + user_principal_name = "test-user@ntnxlab.local" + directory_service_reference { + uuid = "" + } + } +} + +data "nutanix_user" "users" {} +``` + + +## Attribute Reference + +The following attributes are exported: + +# Entities + +The entities attribute element contains the following attributes: + +* `metadata`: - The vm kind metadata. +* `api_version` - The version of the API. +* `state`: - The state of the entity. +* `name`: - The name of the user. +* `user_type`: - The name of the user. +* `display_name`: - The display name of the user (common name) provided by the directory service. +* `project_reference_list`: - A list of projects the user is part of. See #reference for more details. +* `access_control_policy_reference_list`: - List of ACP references. See #reference for more details. +* `directory_service_user`: - (Optional) The directory service user configuration. See below for more information. +* `identity_provider_user`: - (Optional) (Optional) The identity provider user configuration. See below for more information. +* `categories`: - (Optional) Categories for the Access Control Policy. +* `project_reference`: - (Optional) The reference to a project. +* `owner_reference`: - (Optional) The reference to a user. + +### Metadata + +The metadata attribute exports the following: + +* `last_update_time`: - UTC date and time in RFC-3339 format when vm was last updated. +* `uuid`: - vm UUID. +* `creation_time`: - UTC date and time in RFC-3339 format when vm was created. +* `spec_version`: - Version number of the latest spec. +* `spec_hash`: - Hash of the spec. This will be returned from server. +* `name`: - vm name. +* `should_force_translate`: - Applied on Prism Central only. Indicate whether force to translate the spec of the fanout request to fit the target cluster API schema. + +### Categories + +The categories attribute supports the following: + +* `name`: - the key name. +* `value`: - value of the key. + +### Directory Service User + +The directory_service_user argument supports the following: + +* `user_principal_name`: - (Optional) The UserPrincipalName of the user from the directory service. +* `directory_service_reference`: - (Optional) The reference to a directory service. See #reference for to look the supported attributes. + +### Identity Provider User + +The identity_provider_user argument supports the following: + +* `username`: - (Optional) The username from identity provider. Name ID for SAML Identity Provider. +* `identity_provider_reference`: - (Optional) The reference to a identity provider. See #reference for to look the supported attributes. + +### Reference + +The `project_reference`, `owner_reference`, `role_reference` `directory_service_reference` attributes supports the following: + +* `kind`: - The kind name. (Default depends on the resource you are referencing) +* `name`: - the name(Optional). +* `uuid`: - the UUID(Required). + +For `access_control_policy_reference_list` and `project_reference_list` are the same as reference but used as list. + +See detailed information in [Nutanix Users](https://www.nutanix.dev/reference/prism_central/v3/api/users/). diff --git a/website/docs/r/role.html.markdown b/website/docs/r/role.html.markdown new file mode 100644 index 000000000..608dfaf60 --- /dev/null +++ b/website/docs/r/role.html.markdown @@ -0,0 +1,83 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_role" +sidebar_current: "docs-nutanix-resource-role" +description: |- + This operation submits a request to create a role based on the input parameters. +--- + +# nutanix_role + +Provides a resource to create a role based on the input parameters. + +## Example Usage + +``` hcl +resource "nutanix_role" "test" { + name = "NAME" + description = "DESCRIPTION" + permission_reference_list { + kind = "permission" + uuid = "ID OF PERMISSION" + } + permission_reference_list { + kind = "permission" + uuid = "ID OF PERMISSION" + } + permission_reference_list { + kind = "permission" + uuid = "ID OF PERMISSION" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name`: - (Optional) Name of the role. +* `description`: - (Optional) The description of the association of a role to a user in a given context. +* `categories`: - (Optional) Categories for the role. +* `project_reference`: - (Optional) The reference to a project. +* `owner_reference`: - (Optional) The reference to a user. +* `project_reference`: - (Optional) The reference to a project. +* `permission_reference_list`: - (Required) List of permission references. + +## Attributes Reference + +The following attributes are exported: + +* `metadata`: - The vm kind metadata. +* `api_version` - The version of the API. +* `state`: - The state of the vm. + +### Metadata + +The metadata attribute exports the following: + +* `last_update_time`: - UTC date and time in RFC-3339 format when vm was last updated. +* `uuid`: - vm UUID. +* `creation_time`: - UTC date and time in RFC-3339 format when vm was created. +* `spec_version`: - Version number of the latest spec. +* `spec_hash`: - Hash of the spec. This will be returned from server. +* `name`: - vm name. +* `should_force_translate`: - Applied on Prism Central only. Indicate whether force to translate the spec of the fanout request to fit the target cluster API schema. + +### Categories + +The categories attribute supports the following: + +* `name`: - the key name. +* `value`: - value of the key. + +### Reference + +The `project_reference`, `owner_reference` attributes supports the following: + +* `kind`: - The kind name (Default value: project)(Required). +* `name`: - the name(Optional). +* `uuid`: - the UUID(Required). + +For `permission_reference_list` are the same as reference but used as array. + +See detailed information in [Nutanix Roles](https://www.nutanix.dev/reference/prism_central/v3/api/roles/). diff --git a/website/docs/r/user.html.markdown b/website/docs/r/user.html.markdown new file mode 100644 index 000000000..79247c109 --- /dev/null +++ b/website/docs/r/user.html.markdown @@ -0,0 +1,112 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_user" +sidebar_current: "docs-nutanix-resource-user" +description: |- + This operation submits a request to create a user based on the input parameters. +--- + +# nutanix_user + +Provides a resource to create a user based on the input parameters. + +## Example Usage + +``` hcl +resource "nutanix_user" "user" { + directory_service_user { + user_principal_name = "test-user@ntnxlab.local" + directory_service_reference { + uuid = "" + } + } +} +``` + + +``` hcl +resource "nutanix_user" "user" { + identity_provider_user { + username = "username" + identity_provider_reference { + uuid = "" + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `directory_service_user`: - (Optional) The directory service user configuration. See below for more information. +* `identity_provider_user`: - (Optional) (Optional) The identity provider user configuration. See below for more information. +* `categories`: - (Optional) Categories for the Access Control Policy. +* `project_reference`: - (Optional) The reference to a project. +* `owner_reference`: - (Optional) The reference to a user. +* `project_reference`: - (Optional) The reference to a project. + +### Directory Service User + +The directory_service_user argument supports the following: + +* `user_principal_name`: - (Optional) The UserPrincipalName of the user from the directory service. +* `directory_service_reference`: - (Optional) The reference to a directory service. See #reference for to look the supported attributes. + +### Identity Provider User + +The identity_provider_user argument supports the following: + +* `username`: - (Optional) The username from identity provider. Name ID for SAML Identity Provider. +* `identity_provider_reference`: - (Optional) The reference to a identity provider. See #reference for to look the supported attributes. + +### Context List + +The context_list attribute supports the following: + +* `scope_filter_expression_list`: - (Optional) The device ID which is used to uniquely identify this particular disk. +* `entity_filter_expression_list` - (Required) A list of Entity filter expressions. + +## Attributes Reference + +The following attributes are exported: + +* `metadata`: - The vm kind metadata. +* `api_version` - The version of the API. +* `state`: - The state of the entity. +* `name`: - The name of the user. +* `user_type`: - The name of the user. +* `display_name`: - The display name of the user (common name) provided by the directory service. +* `project_reference_list`: - A list of projects the user is part of. See #reference for more details. +* `access_control_policy_reference_list`: - List of ACP references. See #reference for more details. + +### Metadata + +The metadata attribute exports the following: + +* `last_update_time`: - UTC date and time in RFC-3339 format when vm was last updated. +* `uuid`: - vm UUID. +* `creation_time`: - UTC date and time in RFC-3339 format when vm was created. +* `spec_version`: - Version number of the latest spec. +* `spec_hash`: - Hash of the spec. This will be returned from server. +* `name`: - vm name. +* `should_force_translate`: - Applied on Prism Central only. Indicate whether force to translate the spec of the fanout request to fit the target cluster API schema. + +### Categories + +The categories attribute supports the following: + +* `name`: - the key name. +* `value`: - value of the key. + +### Reference + +The `project_reference`, `owner_reference`, `role_reference` `directory_service_reference` attributes supports the following: + +* `kind`: - The kind name. (Default depends on the resource you are referencing) +* `name`: - the name(Optional). +* `uuid`: - the UUID(Required). + +For `access_control_policy_reference_list` and `project_reference_list` are the same as reference but used as list. + +See detailed information in [Nutanix Users](https://www.nutanix.dev/reference/prism_central/v3/api/users/). diff --git a/website/nutanix.erb b/website/nutanix.erb index 645368223..3718f9c56 100644 --- a/website/nutanix.erb +++ b/website/nutanix.erb @@ -4,69 +4,81 @@ <% end %>