From 8117af025bfb964afccff86810a884eb643654b9 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Tue, 31 Dec 2024 12:03:31 +0000 Subject: [PATCH 01/21] re-work after RBACRole changed shape on REST api. Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- docs/generated/errors-list.md | 11 +- pkg/errors/errorMessage.go | 11 +- pkg/roles/roles.go | 8 +- pkg/roles/rolesGet.go | 100 +--- pkg/roles/rolesGet_test.go | 609 +++++--------------- pkg/roles/roles_test.go | 68 +++ pkg/rolesformatter/summaryFormatter.go | 2 - pkg/rolesformatter/summaryFormatter_test.go | 140 ++--- pkg/rolesformatter/yamlFormatter_test.go | 97 ++-- 9 files changed, 333 insertions(+), 713 deletions(-) create mode 100644 pkg/roles/roles_test.go diff --git a/docs/generated/errors-list.md b/docs/generated/errors-list.md index 20f9047e..88ab87f8 100644 --- a/docs/generated/errors-list.md +++ b/docs/generated/errors-list.md @@ -204,15 +204,8 @@ The `galasactl` tool can generate the following errors: - GAL1206E: Failed to get roles. Unexpected http status code {} received from the server. Error details from the server are not in a valid json format. Cause: '{}' - GAL1207E: Failed to get roles. Unexpected http status code {} received from the server. Error details from the server are: '{}' - GAL1208E: Failed to get roles. Unexpected http status code {} received from the server. Error details from the server are not in the json format. -- GAL1209E: An attempt to get a role named '{}' failed. Unexpected http status code {} received from the server. -- GAL1210E: An attempt to get a role named '{}' failed. Unexpected http status code {} received from the server. Error details from the server could not be read. Cause: {} -- GAL1211E: An attempt to get a role named '{}' failed. Unexpected http status code {} received from the server. Error details from the server are not in a valid json format. Cause: '{}' -- GAL1212E: An attempt to get a role named '{}' failed. Unexpected http status code {} received from the server. Error details from the server are: '{}' -- GAL1213E: An attempt to get a role named '{}' failed. Unexpected http status code {} received from the server. Error details from the server are not in the json format. -- GAL1214E: An attempt to get a role named '{}' failed. Sending the get request to the Galasa service failed. Cause is {} -- GAL1215E: Invalid role name provided. The name provided with the --name flag cannot be empty, contain spaces or dots (.), and must only contain characters in the Latin-1 character set. -- GAL1216E: Role name {} is not known on the Galasa service. -- GAL1217E: Role output format {} is not usable when a list of roles is requested. Use the --name flag if you wish to see a role in yaml format. +- GAL1209E: Invalid role name provided. The name provided with the --name flag cannot be empty, contain spaces or dots (.), and must only contain characters in the Latin-1 character set. +- GAL1210E: Role name {} is not known on the Galasa service. - GAL1225E: Failed to open file '{}' cause: {}. Check that this file exists, and that you have read permissions. - GAL1226E: Internal failure. Contents of gzip could be read, but not decoded. New gzip reader failed: file: {} error: {} - GAL1227E: Internal failure. Contents of gzip could not be decoded. {} error: {} diff --git a/pkg/errors/errorMessage.go b/pkg/errors/errorMessage.go index 2e7957a3..4759a8ce 100644 --- a/pkg/errors/errorMessage.go +++ b/pkg/errors/errorMessage.go @@ -390,15 +390,8 @@ var ( GALASA_ERROR_GET_ROLES_EXPLANATION_NOT_JSON = NewMessageType("GAL1208E: Failed to get roles. Unexpected http status code %v received from the server. Error details from the server are not in the json format.", 1208, STACK_TRACE_NOT_WANTED) // When getting a single named role... - GALASA_ERROR_GET_ROLE_NO_RESPONSE_CONTENT = NewMessageType("GAL1209E: An attempt to get a role named '%s' failed. Unexpected http status code %v received from the server.", 1209, STACK_TRACE_NOT_WANTED) - GALASA_ERROR_GET_ROLE_RESPONSE_BODY_UNREADABLE = NewMessageType("GAL1210E: An attempt to get a role named '%s' failed. Unexpected http status code %v received from the server. Error details from the server could not be read. Cause: %s", 1210, STACK_TRACE_NOT_WANTED) - GALASA_ERROR_GET_ROLE_UNPARSEABLE_CONTENT = NewMessageType("GAL1211E: An attempt to get a role named '%s' failed. Unexpected http status code %v received from the server. Error details from the server are not in a valid json format. Cause: '%s'", 1211, STACK_TRACE_NOT_WANTED) - GALASA_ERROR_GET_ROLE_SERVER_REPORTED_ERROR = NewMessageType("GAL1212E: An attempt to get a role named '%s' failed. Unexpected http status code %v received from the server. Error details from the server are: '%s'", 1212, STACK_TRACE_NOT_WANTED) - GALASA_ERROR_GET_ROLE_EXPLANATION_NOT_JSON = NewMessageType("GAL1213E: An attempt to get a role named '%s' failed. Unexpected http status code %v received from the server. Error details from the server are not in the json format.", 1213, STACK_TRACE_NOT_WANTED) - GALASA_ERROR_GET_ROLE_REQUEST_FAILED = NewMessageType("GAL1214E: An attempt to get a role named '%s' failed. Sending the get request to the Galasa service failed. Cause is %v", 1214, STACK_TRACE_NOT_WANTED) - GALASA_ERROR_INVALID_ROLE_NAME = NewMessageType("GAL1215E: Invalid role name provided. The name provided with the --name flag cannot be empty, contain spaces or dots (.), and must only contain characters in the Latin-1 character set.", 1215, STACK_TRACE_NOT_WANTED) - GALASA_ERROR_ROLE_NAME_NOT_FOUND = NewMessageType("GAL1216E: Role name %v is not known on the Galasa service.", 1216, STACK_TRACE_NOT_WANTED) - GALASA_ERROR_ROLES_LIST_NOT_COMPATIBLE_WITH_YAML_FORMAT = NewMessageType("GAL1217E: Role output format %v is not usable when a list of roles is requested. Use the --name flag if you wish to see a role in yaml format.", 1217, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_INVALID_ROLE_NAME = NewMessageType("GAL1209E: Invalid role name provided. The name provided with the --name flag cannot be empty, contain spaces or dots (.), and must only contain characters in the Latin-1 character set.", 1209, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_ROLE_NAME_NOT_FOUND = NewMessageType("GAL1210E: Role name %v is not known on the Galasa service.", 1210, STACK_TRACE_NOT_WANTED) // Warnings... GALASA_WARNING_MAVEN_NO_GALASA_OBR_REPO = NewMessageType("GAL2000W: Warning: Maven configuration file settings.xml should contain a reference to a Galasa repository so that the galasa OBR can be resolved. The official release repository is '%s', and 'pre-release' repository is '%s'", 2000, STACK_TRACE_WANTED) diff --git a/pkg/roles/roles.go b/pkg/roles/roles.go index e85276ac..6bb08f53 100644 --- a/pkg/roles/roles.go +++ b/pkg/roles/roles.go @@ -11,9 +11,9 @@ import ( galasaErrors "github.com/galasa-dev/cli/pkg/errors" ) -func validateRoleName(name string) (string, error) { +func validateRoleName(nameToValidate string) (string, error) { var err error - name = strings.TrimSpace(name) + name := strings.TrimSpace(nameToValidate) if name == "" || strings.ContainsAny(name, " .\n\t") || !isLatin1(name) { err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_ROLE_NAME) @@ -26,7 +26,9 @@ func validateRoleName(name string) (string, error) { func isLatin1(str string) bool { isValidLatin1 := true for _, character := range str { - if character > 255 { + if !((character >= 'a' && character <= 'z') || + (character >= 'A' && character <= 'Z') || + (character >= '0' && character <= '9')) { isValidLatin1 = false break } diff --git a/pkg/roles/rolesGet.go b/pkg/roles/rolesGet.go index 0f2d8aed..ff393f6a 100644 --- a/pkg/roles/rolesGet.go +++ b/pkg/roles/rolesGet.go @@ -34,8 +34,9 @@ func GetRoles( var chosenFormatter rolesformatter.RolesFormatter roles := make([]galasaapi.RBACRole, 0) - chosenFormatter, err = validateFormatFlag(format, roleName) + chosenFormatter, err = validateFormatFlag(format) if err == nil { + log.Printf("formatter flag is valid.\n") if roleName != "" { // The user has provided a Role name, so try to get that Role var role *galasaapi.RBACRole @@ -68,6 +69,7 @@ func getRoleByName( ) (*galasaapi.RBACRole, error) { var err error var role *galasaapi.RBACRole + roleName, err = validateRoleName(roleName) if err == nil { role, err = getRoleFromRestApiGivenName(roleName, apiClient, byteReader) @@ -81,69 +83,15 @@ func getRoleFromRestApiGivenName( apiClient *galasaapi.APIClient, byteReader spi.ByteReader, ) (*galasaapi.RBACRole, error) { - var err error var role *galasaapi.RBACRole - roleId, err := roleNameToRoleId(roleName, apiClient, byteReader) - if err == nil { - role, err = getRoleFromRestApiGivenId(roleId, apiClient, byteReader) - } - return role, err -} - -func getRoleFromRestApiGivenId( - roleId string, - apiClient *galasaapi.APIClient, - byteReader spi.ByteReader, -) (*galasaapi.RBACRole, error) { - var err error - var httpResponse *http.Response - var context context.Context = context.Background() - var restApiVersion string - var role *galasaapi.RBACRole - - restApiVersion, err = embedded.GetGalasactlRestApiVersion() - if err == nil { - role, httpResponse, err = apiClient.RoleBasedAccessControlAPIApi.GetRBACRole(context, roleId). - ClientApiVersion(restApiVersion). - Execute() - - if httpResponse != nil { - defer httpResponse.Body.Close() - } - - if err != nil { - if httpResponse == nil { - err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_GET_ROLE_REQUEST_FAILED, err.Error()) - } else { - err = galasaErrors.HttpResponseToGalasaError( - httpResponse, - roleId, - byteReader, - galasaErrors.GALASA_ERROR_GET_ROLE_NO_RESPONSE_CONTENT, - galasaErrors.GALASA_ERROR_GET_ROLE_RESPONSE_BODY_UNREADABLE, - galasaErrors.GALASA_ERROR_GET_ROLE_UNPARSEABLE_CONTENT, - galasaErrors.GALASA_ERROR_GET_ROLE_SERVER_REPORTED_ERROR, - galasaErrors.GALASA_ERROR_GET_ROLE_EXPLANATION_NOT_JSON, - ) - } - } - } - return role, err -} - -func roleNameToRoleId(roleName string, apiClient *galasaapi.APIClient, byteReader spi.ByteReader) (string, error) { - - var roleId string - + // Query all the roles. roles, err := getRolesFromRestApi(apiClient, byteReader) if err == nil { - role, err := findRoleWithName(roles, roleName) - if err == nil { - roleId = *(role.Metadata).Id - } + // Find the one with a matching name, or fail. + role, err = findRoleWithName(roles, roleName) } - return roleId, err + return role, err } func findRoleWithName(roles []galasaapi.RBACRole, roleNameToFind string) (*galasaapi.RBACRole, error) { @@ -172,12 +120,12 @@ func getRolesFromRestApi( var context context.Context = context.Background() var restApiVersion string var roles []galasaapi.RBACRole - var rolesMetadata []galasaapi.RBACRoleMetadata restApiVersion, err = embedded.GetGalasactlRestApiVersion() if err == nil { - rolesMetadata, httpResponse, err = apiClient.RoleBasedAccessControlAPIApi.GetRBACRoles(context). + log.Printf("Getting role data from remote service\n") + roles, httpResponse, err = apiClient.RoleBasedAccessControlAPIApi.GetRBACRoles(context). ClientApiVersion(restApiVersion). Execute() @@ -201,34 +149,12 @@ func getRolesFromRestApi( ) } } else { - // The role metadata for all the roles was obtained. - // Turn these into simulated full role records so they can all be formatted with the same code. - // But we must beware, because lots of the data is missing. - // (So we can't output in yaml for example) - roles = createSimulatedRoleRecordsFromRolesMetadata(rolesMetadata) + log.Printf("Got back %v roles %v", len(roles), roles) } } return roles, err } -func createSimulatedRoleRecordsFromRolesMetadata(rolesMetadata []galasaapi.RBACRoleMetadata) []galasaapi.RBACRole { - var roles []galasaapi.RBACRole = make([]galasaapi.RBACRole, 0) - // Note: We use the index here and de-reference it ourselves on purpose. - // This is subtle: - // If we used the value, then taking the address of that value will result in filling the roles array returned - // with records which all point to the same variable defined in this scope. The variable is defined only once, - // so it essentially creates an array of values where each one is referencing the same metadata instance. - // If we didn't do that we would have to create our own metadata structure, and copy fields from one to the other. - for index := range rolesMetadata { - role := galasaapi.RBACRole{} - role.Metadata = &(rolesMetadata[index]) - roles = append(roles, role) - - log.Printf("roles: %v\n", roles) - } - return roles -} - func createFormatters() map[string]rolesformatter.RolesFormatter { formatters := make(map[string]rolesformatter.RolesFormatter, 0) summaryFormatter := rolesformatter.NewRolesSummaryFormatter() @@ -259,7 +185,7 @@ func GetFormatterNamesAsString() string { return formatterNames.String() } -func validateFormatFlag(outputFormatString string, roleName string) (rolesformatter.RolesFormatter, error) { +func validateFormatFlag(outputFormatString string) (rolesformatter.RolesFormatter, error) { var err error chosenFormatter, isPresent := formatters[outputFormatString] @@ -268,9 +194,5 @@ func validateFormatFlag(outputFormatString string, roleName string) (rolesformat err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_OUTPUT_FORMAT, outputFormatString, GetFormatterNamesAsString()) } - if roleName == "" && chosenFormatter.GetName() == rolesformatter.YAML_FORMATTER_NAME { - err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_ROLES_LIST_NOT_COMPATIBLE_WITH_YAML_FORMAT, outputFormatString) - } - return chosenFormatter, err } diff --git a/pkg/roles/rolesGet_test.go b/pkg/roles/rolesGet_test.go index 418edab9..1ecd57dd 100644 --- a/pkg/roles/rolesGet_test.go +++ b/pkg/roles/rolesGet_test.go @@ -5,76 +5,71 @@ */ package roles -const ( - API_VERSION = "galasa-dev/v1alpha1" - DUMMY_ENCODING = "myencoding" - DUMMY_USERNAME = "dummy-username" - DUMMY_PASSWORD = "dummy-password" +import ( + "encoding/json" + "net/http" + "testing" + + "github.com/galasa-dev/cli/pkg/api" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" ) -/* -func createMockGalasaSecret(secretName string, description string) galasaapi.GalasaSecret { - secret := *galasaapi.NewGalasaSecret() +const ( + API_VERSION = "galasa-dev/v1alpha1" +) - secret.SetApiVersion(API_VERSION) - secret.SetKind("GalasaSecret") +func createTestGalasaRole(id string, name string, description string) galasaapi.RBACRole { + role := *galasaapi.NewRBACRole() - secretMetadata := *galasaapi.NewGalasaSecretMetadata() - secretMetadata.SetName(secretName) - secretMetadata.SetEncoding(DUMMY_ENCODING) - secretMetadata.SetType("UsernamePassword") - secretMetadata.SetLastUpdatedBy(DUMMY_USERNAME) - secretMetadata.SetLastUpdatedTime(time.Date(2024, 01, 01, 10, 0, 0, 0, time.UTC)) + role.SetApiVersion(API_VERSION) + role.SetKind("GalasaRole") + metadata := *galasaapi.NewRBACRoleMetadata() + metadata.SetName(name) + metadata.SetId(id) if description != "" { - secretMetadata.SetDescription(description) + metadata.SetDescription(description) } - secretData := *galasaapi.NewGalasaSecretData() - secretData.SetUsername(DUMMY_USERNAME) - secretData.SetPassword(DUMMY_PASSWORD) + data := *galasaapi.NewRBACRoleData() + actionStrings := make([]string, 2) + actionStrings[0] = "action1" + actionStrings[1] = "action2" + data.SetActions(actionStrings) - secret.SetMetadata(secretMetadata) - secret.SetData(secretData) - return secret -} + role.SetMetadata(metadata) + role.SetData(data) -func generateExpectedSecretYaml(secretName string, description string) string { - return fmt.Sprintf(`apiVersion: %s -kind: GalasaSecret -metadata: - name: %s - description: %s - lastUpdatedTime: 2024-01-01T10:00:00Z - lastUpdatedBy: %s - encoding: %s - type: UsernamePassword -data: - username: %s - password: %s`, API_VERSION, secretName, description, DUMMY_USERNAME, DUMMY_ENCODING, DUMMY_USERNAME, DUMMY_PASSWORD) + return role } -func TestCanGetASecretByName(t *testing.T) { +func TestGetANamedRoleWhenRoleExistsFindsItOkSummaryFormat(t *testing.T) { // Given... - secretName := "SYSTEM1" - description := "my SYSTEM1 secret" + roleId := "role1" + roleName := "role1Name" + description := "role1Description" outputFormat := "summary" - // Create the mock secret to return - secret := createMockGalasaSecret(secretName, description) - secretBytes, _ := json.Marshal(secret) - secretJson := string(secretBytes) - - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets/"+secretName, http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + // Create the test role array to return + role := createTestGalasaRole(roleId, roleName, description) + roles := make([]galasaapi.RBACRole, 0) + roles = append(roles, role) + rolesBytes, _ := json.Marshal(roles) + rolesJson := string(rolesBytes) + + // Create the expected HTTP interactions with the API server. + // We expect it to call /rbac/roles to get all the roles, then use that to find the one we name. + getRoleInteraction := utils.NewHttpInteraction("/rbac/roles", http.MethodGet) + getRoleInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) - writer.Write([]byte(secretJson)) + writer.Write([]byte(rolesJson)) } interactions := []utils.HttpInteraction{ - getSecretInteraction, + getRoleInteraction, } server := utils.NewMockHttpServer(t, interactions) @@ -87,7 +82,7 @@ func TestCanGetASecretByName(t *testing.T) { // When... err := GetRoles( - secretName, + roleName, outputFormat, console, apiClient, @@ -95,89 +90,41 @@ func TestCanGetASecretByName(t *testing.T) { // Then... expectedOutput := - `name description -SYSTEM1 UsernamePassword 2024-01-01 10:00:00 dummy-username my SYSTEM1 secret + `name description +role1Name role1Description Total:1 ` - assert.Nil(t, err, "GetSecrets returned an unexpected error") + assert.Nil(t, err, "GetRoles returned an unexpected error") assert.Equal(t, expectedOutput, console.ReadText()) } -func TestCanGetASecretByNameInYamlFormat(t *testing.T) { +func TestCanGetAllRolesOkSummaryFormat(t *testing.T) { // Given... - secretName := "SYSTEM1" - description := "my SYSTEM1 secret" - outputFormat := "yaml" - - // Create the mock secret to return - secret := createMockGalasaSecret(secretName, description) - secretBytes, _ := json.Marshal(secret) - secretJson := string(secretBytes) - - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets/"+secretName, http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusOK) - writer.Write([]byte(secretJson)) - } - - interactions := []utils.HttpInteraction{ - getSecretInteraction, - } - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - console := utils.NewMockConsole() - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := GetRoles( - secretName, - outputFormat, - console, - apiClient, - mockByteReader) - - // Then... - expectedOutput := generateExpectedSecretYaml(secretName, description) + "\n" - assert.Nil(t, err, "GetSecrets returned an unexpected error") - assert.Equal(t, expectedOutput, console.ReadText()) -} - -func TestCanGetAllSecretsOk(t *testing.T) { - // Given... - // Don't provide a secret name so that we can get all secrets - secretName := "" + roleId := "role1" + roleName := "role1Name" + description := "role1Description" outputFormat := "summary" - - // Create the mock secret to return - secrets := make([]galasaapi.GalasaSecret, 0) - secret1Name := "BOB" - secret2Name := "BLAH" - description1 := "my BOB secret" - description2 := "my BLAH secret" - secret1 := createMockGalasaSecret(secret1Name, description1) - secret2 := createMockGalasaSecret(secret2Name, description2) - - secrets = append(secrets, secret1, secret2) - secretsBytes, _ := json.Marshal(secrets) - secretsJson := string(secretsBytes) - - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets", http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + roleNameToLookFor := "" + + // Create the test role array to return + role := createTestGalasaRole(roleId, roleName, description) + roles := make([]galasaapi.RBACRole, 0) + roles = append(roles, role) + rolesBytes, _ := json.Marshal(roles) + rolesJson := string(rolesBytes) + + // Create the expected HTTP interactions with the API server. + // We expect it to call /rbac/roles to get all the roles, then use that to find the one we name. + getRoleInteraction := utils.NewHttpInteraction("/rbac/roles", http.MethodGet) + getRoleInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) - writer.Write([]byte(secretsJson)) + writer.Write([]byte(rolesJson)) } interactions := []utils.HttpInteraction{ - getSecretInteraction, + getRoleInteraction, } server := utils.NewMockHttpServer(t, interactions) @@ -190,7 +137,7 @@ func TestCanGetAllSecretsOk(t *testing.T) { // When... err := GetRoles( - secretName, + roleNameToLookFor, outputFormat, console, apiClient, @@ -198,213 +145,40 @@ func TestCanGetAllSecretsOk(t *testing.T) { // Then... expectedOutput := - `name type last-updated(UTC) last-updated-by description -BOB UsernamePassword 2024-01-01 10:00:00 dummy-username my BOB secret -BLAH UsernamePassword 2024-01-01 10:00:00 dummy-username my BLAH secret + `name description +role1Name role1Description -Total:2 +Total:1 ` - assert.Nil(t, err, "GetSecrets returned an unexpected error") + assert.Nil(t, err, "GetRoles returned an unexpected error") assert.Equal(t, expectedOutput, console.ReadText()) } -func TestGetASecretWithUnknownFormatDisplaysError(t *testing.T) { - // Given... - secretName := "MYSECRET" - outputFormat := "UNKNOWN FORMAT!" - - // The client-side validation should fail, so no HTTP interactions will be performed - interactions := []utils.HttpInteraction{} - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - console := utils.NewMockConsole() - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := GetRoles( - secretName, - outputFormat, - console, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "GetSecrets did not return an error as expected") - consoleOutputText := err.Error() - assert.Contains(t, consoleOutputText, "GAL1067E") - assert.Contains(t, consoleOutputText, "Unsupported value 'UNKNOWN FORMAT!'") - assert.Contains(t, consoleOutputText, "'summary', 'yaml'") -} - -func TestGetASecretWithBlankNameDisplaysError(t *testing.T) { - // Given... - secretName := " " - outputFormat := "summary" - - // The client-side validation should fail, so no HTTP interactions will be performed - interactions := []utils.HttpInteraction{} - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - console := utils.NewMockConsole() - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := GetRoles( - secretName, - outputFormat, - console, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "GetSecrets did not return an error as expected") - consoleOutputText := err.Error() - assert.Contains(t, consoleOutputText, "GAL1172E") - assert.Contains(t, consoleOutputText, " Invalid secret name provided") -} - -func TestGetNonExistantSecretDisplaysError(t *testing.T) { - // Given... - nonExistantSecret := "secretDoesNotExist123" - outputFormat := "summary" - - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets/"+nonExistantSecret, http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusNotFound) - writer.Write([]byte(`{ "error_message": "No such secret exists" }`)) - } - - interactions := []utils.HttpInteraction{getSecretInteraction} - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - console := utils.NewMockConsole() - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := GetRoles( - nonExistantSecret, - outputFormat, - console, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "SecretsGet did not return an error but it should have") - consoleOutputText := err.Error() - assert.Contains(t, consoleOutputText, nonExistantSecret) - assert.Contains(t, consoleOutputText, "GAL1177E") - assert.Contains(t, consoleOutputText, "Error details from the server are: 'No such secret exists'") -} - -func TestSecretsGetFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { - // Given... - secretName := "MYSECRET" - outputFormat := "summary" - - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets/"+secretName, http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - writer.WriteHeader(http.StatusInternalServerError) - } - - interactions := []utils.HttpInteraction{ - getSecretInteraction, - } - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - console := utils.NewMockConsole() - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := GetRoles( - secretName, - outputFormat, - console, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "SecretsGet did not return an error but it should have") - errorMsg := err.Error() - assert.Contains(t, errorMsg, secretName) - assert.Contains(t, errorMsg, "GAL1174E") -} - -func TestSecretsGetFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { +func TestCanGetARoleByIdWhenRoleExistsFindsItOkInYamlFormat(t *testing.T) { // Given... - secretName := "MYSECRET" - outputFormat := "summary" - - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets/"+secretName, http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - writer.WriteHeader(http.StatusInternalServerError) - writer.Header().Set("Content-Type", "application/notJsonOnPurpose") - writer.Write([]byte("something not json but non-zero-length.")) - } - - interactions := []utils.HttpInteraction{ - getSecretInteraction, - } - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - console := utils.NewMockConsole() - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := GetRoles( - secretName, - outputFormat, - console, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "SecretsGet did not return an error but it should have") - errorMsg := err.Error() - assert.Contains(t, errorMsg, secretName) - assert.Contains(t, errorMsg, strconv.Itoa(http.StatusInternalServerError)) - assert.Contains(t, errorMsg, "GAL1178E") - assert.Contains(t, errorMsg, "Error details from the server are not in the json format") -} - -func TestSecretsGetFailsWithBadlyFormedJsonContentExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { - // Given... - secretName := "MYSECRET" - outputFormat := "summary" + roleId := "role1" + roleName := "role1Name" + description := "role1Description" + outputFormat := "yaml" - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets/"+secretName, http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + // Create the test role array to return + role := createTestGalasaRole(roleId, roleName, description) + roles := make([]galasaapi.RBACRole, 0) + roles = append(roles, role) + rolesBytes, _ := json.Marshal(roles) + rolesJson := string(rolesBytes) + + // Create the expected HTTP interactions with the API server. + // We expect it to call /rbac/roles to get all the roles, then use that to find the one we name. + getRoleInteraction := utils.NewHttpInteraction("/rbac/roles", http.MethodGet) + getRoleInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusInternalServerError) - writer.Write([]byte(`{ "this": "isBadJson because it doesnt end in a close braces" `)) + writer.WriteHeader(http.StatusOK) + writer.Write([]byte(rolesJson)) } interactions := []utils.HttpInteraction{ - getSecretInteraction, + getRoleInteraction, } server := utils.NewMockHttpServer(t, interactions) @@ -417,77 +191,55 @@ func TestSecretsGetFailsWithBadlyFormedJsonContentExplanationErrorPayloadGivesCo // When... err := GetRoles( - secretName, + roleName, outputFormat, console, apiClient, mockByteReader) // Then... - assert.NotNil(t, err, "SecretsGet did not return an error but it should have") - errorMsg := err.Error() - assert.Contains(t, errorMsg, secretName) - assert.Contains(t, errorMsg, strconv.Itoa(http.StatusInternalServerError)) - assert.Contains(t, errorMsg, "GAL1176E") - assert.Contains(t, errorMsg, "Error details from the server are not in a valid json format") - assert.Contains(t, errorMsg, "Cause: 'unexpected end of JSON input'") + expectedOutput := + `apiversion: galasa-dev/v1alpha1 +kind: GalasaRole +metadata: + id: role1 + name: role1Name + description: role1Description + url: null +data: + actions: + - action1 + - action2 +` + assert.Nil(t, err, "GetRoles returned an unexpected error") + assert.Equal(t, expectedOutput, console.ReadText()) } -func TestSecretsGetFailsWithFailureToReadResponseBodyGivesCorrectMessage(t *testing.T) { +func TestCanGetARoleByIdWhenRoleDoesNotExistCausesError(t *testing.T) { // Given... - secretName := "MYSECRET" + roleId := "role1" + roleName := "role1Name" + description := "role1Description" outputFormat := "summary" - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets/"+secretName, http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + // Create the test role array to return + role := createTestGalasaRole(roleId, roleName, description) + roles := make([]galasaapi.RBACRole, 0) + roles = append(roles, role) + rolesBytes, _ := json.Marshal(roles) + rolesJson := string(rolesBytes) + + // Create the expected HTTP interactions with the API server. + // We expect it to call /rbac/roles to get all the roles, then use that to find the one we name. + getRoleInteraction := utils.NewHttpInteraction("/rbac/roles", http.MethodGet) + getRoleInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusInternalServerError) - writer.Write([]byte(`{}`)) - } - - interactions := []utils.HttpInteraction{ - getSecretInteraction, - } - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - console := utils.NewMockConsole() - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReaderAsMock(true) - - // When... - err := GetRoles( - secretName, - outputFormat, - console, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "SecretsGet returned an unexpected error") - errorMsg := err.Error() - assert.Contains(t, errorMsg, secretName) - assert.Contains(t, errorMsg, strconv.Itoa(http.StatusInternalServerError)) - assert.Contains(t, errorMsg, "GAL1175E") - assert.Contains(t, errorMsg, "Error details from the server could not be read") -} - -func TestGetAllSecretsFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { - // Given... - secretName := "" - outputFormat := "summary" - - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets", http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - writer.WriteHeader(http.StatusInternalServerError) + writer.WriteHeader(http.StatusOK) + writer.Write([]byte(rolesJson)) } interactions := []utils.HttpInteraction{ - getSecretInteraction, + getRoleInteraction, } server := utils.NewMockHttpServer(t, interactions) @@ -500,34 +252,21 @@ func TestGetAllSecretsFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *t // When... err := GetRoles( - secretName, + "NotTheCorrectName", outputFormat, console, apiClient, mockByteReader) // Then... - assert.NotNil(t, err, "SecretsGet did not return an error but it should have") - errorMsg := err.Error() - assert.Contains(t, errorMsg, "GAL1180E") + // We expect an error. + assert.NotNil(t, err, "GetRoles didnt return an expected error") + assert.Contains(t, err.Error(), "GAL1210E") } -func TestGetAllSecretsFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { - // Given... - secretName := "" - outputFormat := "summary" - - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets", http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - writer.WriteHeader(http.StatusInternalServerError) - writer.Header().Set("Content-Type", "application/notJsonOnPurpose") - writer.Write([]byte("something not json but non-zero-length.")) - } - - interactions := []utils.HttpInteraction{ - getSecretInteraction, - } +func TestTryGettingAnythingWithAnInvalidFormatterNameFailsImmediately(t *testing.T) { + // Not expecting any iteractions... + interactions := []utils.HttpInteraction{} server := utils.NewMockHttpServer(t, interactions) defer server.Server.Close() @@ -537,37 +276,33 @@ func TestGetAllSecretsFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCor apiClient := api.InitialiseAPI(apiServerUrl) mockByteReader := utils.NewMockByteReader() - // When... err := GetRoles( - secretName, - outputFormat, + "validRoleName", + "unknownOutputFormatInvalid", console, apiClient, mockByteReader) - // Then... - assert.NotNil(t, err, "SecretsGet did not return an error but it should have") - errorMsg := err.Error() - assert.Contains(t, errorMsg, strconv.Itoa(http.StatusInternalServerError)) - assert.Contains(t, errorMsg, "GAL1184E") - assert.Contains(t, errorMsg, "Error details from the server are not in the json format") + assert.NotNil(t, err, "Expected an error, didn't get one!") + assert.Contains(t, err.Error(), "GAL1067E") } -func TestGetAllSecretsFailsWithBadlyFormedJsonContentExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { +func TestCanGetARoleByIdWhenRoleExistsFindsItOkSummaryFormat(t *testing.T) { // Given... - secretName := "" + roleName := "role1Name" outputFormat := "summary" - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets", http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + // Create the expected HTTP interactions with the API server. + // We expect it to call /rbac/roles to get all the roles, then use that to find the one we name. + getRoleInteraction := utils.NewHttpInteraction("/rbac/roles", http.MethodGet) + getRoleInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusInternalServerError) - writer.Write([]byte(`{ "this": "isBadJson because it doesnt end in a close braces" `)) + writer.WriteHeader(http.StatusOK) + writer.Write([]byte("Not valid json format output from fake service")) } interactions := []utils.HttpInteraction{ - getSecretInteraction, + getRoleInteraction, } server := utils.NewMockHttpServer(t, interactions) @@ -580,59 +315,13 @@ func TestGetAllSecretsFailsWithBadlyFormedJsonContentExplanationErrorPayloadGive // When... err := GetRoles( - secretName, - outputFormat, - console, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "SecretsGet did not return an error but it should have") - errorMsg := err.Error() - assert.Contains(t, errorMsg, strconv.Itoa(http.StatusInternalServerError)) - assert.Contains(t, errorMsg, "GAL1182E") - assert.Contains(t, errorMsg, "Error details from the server are not in a valid json format") - assert.Contains(t, errorMsg, "Cause: 'unexpected end of JSON input'") -} - -func TestGetAllSecretsFailsWithFailureToReadResponseBodyGivesCorrectMessage(t *testing.T) { - // Given... - secretName := "" - outputFormat := "summary" - - // Create the expected HTTP interactions with the API server - getSecretInteraction := utils.NewHttpInteraction("/secrets", http.MethodGet) - getSecretInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusInternalServerError) - writer.Write([]byte(`{}`)) - } - - interactions := []utils.HttpInteraction{ - getSecretInteraction, - } - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - console := utils.NewMockConsole() - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReaderAsMock(true) - - // When... - err := GetRoles( - secretName, + roleName, outputFormat, console, apiClient, mockByteReader) // Then... - assert.NotNil(t, err, "SecretsGet returned an unexpected error") - errorMsg := err.Error() - assert.Contains(t, errorMsg, strconv.Itoa(http.StatusInternalServerError)) - assert.Contains(t, errorMsg, "GAL1181E") - assert.Contains(t, errorMsg, "Error details from the server could not be read") + assert.NotNil(t, err, "GetRoles returned an no error when one was expected") + assert.Contains(t, err.Error(), "GAL1206E") } -*/ diff --git a/pkg/roles/roles_test.go b/pkg/roles/roles_test.go new file mode 100644 index 00000000..b84c38ad --- /dev/null +++ b/pkg/roles/roles_test.go @@ -0,0 +1,68 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package roles + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidRoleNameValidatesOk(t *testing.T) { + roleNamePutIn := "validRole" + roleNameGotBack, err := validateRoleName(roleNamePutIn) + assert.Nil(t, err, "No error expected but there was one reported") + assert.Equal(t, roleNameGotBack, "validRole") +} + +func TestValidRoleNameWithSpacesValidatesOkAndGetsTrimmedAtFront(t *testing.T) { + roleNamePutIn := " validRole" + roleNameGotBack, err := validateRoleName(roleNamePutIn) + assert.Nil(t, err, "No error expected but there was one reported") + assert.Equal(t, roleNameGotBack, "validRole") +} + +func TestValidRoleNameWithSpacesValidatesOkAndGetsTrimmedAtBack(t *testing.T) { + roleNamePutIn := "validRole " + roleNameGotBack, err := validateRoleName(roleNamePutIn) + assert.Nil(t, err, "No error expected but there was one reported") + assert.Equal(t, roleNameGotBack, "validRole") +} + +func TestValidRoleNameWithNumbersValidatesOkAndGetsTrimmedAtBack(t *testing.T) { + roleNamePutIn := "validRole0123456789" + roleNameGotBack, err := validateRoleName(roleNamePutIn) + assert.Nil(t, err, "No error expected but there was one reported") + assert.Equal(t, roleNameGotBack, "validRole0123456789") +} + +func TestValidRoleNameWithDotsValidatesWithErrorReported(t *testing.T) { + roleNamePutIn := "invalid Role with spaces" + _, err := validateRoleName(roleNamePutIn) + assert.NotNil(t, err, "Expected error but there was no error reported") + assert.Contains(t, err.Error(), "GAL1209E") +} + +func TestValidRoleNameWithSpacesValidatesWithErrorReported(t *testing.T) { + roleNamePutIn := "invalid.Role.with.dots" + _, err := validateRoleName(roleNamePutIn) + assert.NotNil(t, err, "Expected error but there was no error reported") + assert.Contains(t, err.Error(), "GAL1209E") +} + +func TestValidRoleNameWithPercentsValidatesWithErrorReported(t *testing.T) { + roleNamePutIn := "invalid%Role%with%dots" + _, err := validateRoleName(roleNamePutIn) + assert.NotNil(t, err, "Expected error but there was no error reported") + assert.Contains(t, err.Error(), "GAL1209E") +} + +func TestValidRoleNameWithWeirdCharactersFailsToValidate(t *testing.T) { + roleNamePutIn := "∞∞∞5%QÑ" + _, err := validateRoleName(roleNamePutIn) + assert.NotNil(t, err, "Expected error but there was no error reported") + assert.Contains(t, err.Error(), "GAL1209E") +} diff --git a/pkg/rolesformatter/summaryFormatter.go b/pkg/rolesformatter/summaryFormatter.go index 869df113..5e78043e 100644 --- a/pkg/rolesformatter/summaryFormatter.go +++ b/pkg/rolesformatter/summaryFormatter.go @@ -6,7 +6,6 @@ package rolesformatter import ( - "log" "strconv" "strings" @@ -50,7 +49,6 @@ func (*RolesSummaryFormatter) FormatRoles(roles []galasaapi.RBACRole) (string, e var line []string name := role.Metadata.GetName() description := role.Metadata.GetDescription() - log.Printf("FormatRoles: adding role name %s\n", name) line = append(line, name, description) table = append(table, line) } diff --git a/pkg/rolesformatter/summaryFormatter_test.go b/pkg/rolesformatter/summaryFormatter_test.go index cf1fb443..a5dc9318 100644 --- a/pkg/rolesformatter/summaryFormatter_test.go +++ b/pkg/rolesformatter/summaryFormatter_test.go @@ -5,118 +5,96 @@ */ package rolesformatter -/* import ( "testing" - "time" "github.com/galasa-dev/cli/pkg/galasaapi" "github.com/stretchr/testify/assert" ) const ( - API_VERSION = "galasa-dev/v1alpha1" - DUMMY_ENCODING = "myencoding" - DUMMY_USERNAME = "dummy-username" - DUMMY_PASSWORD = "dummy-password" + API_VERSION = "galasa-dev/v1alpha1" ) -func createMockGalasaSecretWithDescription( - secretName string, - description string, -) galasaapi.GalasaSecret { - secret := *galasaapi.NewGalasaSecret() - - secret.SetApiVersion(API_VERSION) - secret.SetKind("GalasaSecret") - - secretMetadata := *galasaapi.NewGalasaSecretMetadata() - secretMetadata.SetName(secretName) - secretMetadata.SetEncoding(DUMMY_ENCODING) - secretMetadata.SetType("UsernamePassword") - secretMetadata.SetLastUpdatedBy(DUMMY_USERNAME) - secretMetadata.SetLastUpdatedTime(time.Date(2024, 01, 01, 10, 0, 0, 0, time.UTC)) - - if description != "" { - secretMetadata.SetDescription(description) - } - - secretData := *galasaapi.NewGalasaSecretData() - secretData.SetUsername(DUMMY_USERNAME) - secretData.SetPassword(DUMMY_PASSWORD) - - secret.SetMetadata(secretMetadata) - secret.SetData(secretData) - return secret -} +func createTestRoles() []galasaapi.RBACRole { + + role1 := galasaapi.NewRBACRole() + role1.SetApiVersion(API_VERSION) + role1.SetKind("GalasaRole") + + role1Metadata := *galasaapi.NewRBACRoleMetadata() + role1Metadata.SetName("role1Name") + role1Metadata.SetId("role1Id") + role1Metadata.SetDescription("role1Description") + role1Metadata.SetUrl("http://myHost/api/rbac/roles/role1Id") + role1.Metadata = &role1Metadata + + role1Data := *galasaapi.NewRBACRoleData() + actions1 := make([]string, 0) + actions1 = append(actions1, "action1") + actions1 = append(actions1, "action2") + role1Data.Actions = actions1 + role1.Data = &role1Data + + role2 := galasaapi.NewRBACRole() + role2.SetApiVersion(API_VERSION) + role2.SetKind("GalasaRole") + + role2Metadata := *galasaapi.NewRBACRoleMetadata() + role2Metadata.SetName("role2Name") + role2Metadata.SetId("role2Id") + role2Metadata.SetDescription("role2Description") + role2Metadata.SetUrl("http://myHost/api/rbac/roles/role2Id") + role2.Metadata = &role2Metadata + + role2Data := *galasaapi.NewRBACRoleData() + actions2 := make([]string, 0) + actions2 = append(actions2, "action1") + actions2 = append(actions2, "action2") + role2Data.Actions = actions2 + role2.Data = &role2Data -func TestSecretSummaryFormatterNoDataReturnsTotalCountAllZeros(t *testing.T) { - // Given... - formatter := NewRolesSummaryFormatter() roles := make([]galasaapi.RBACRole, 0) + roles = append(roles, *role1) + roles = append(roles, *role2) - // When... - actualFormattedOutput, err := formatter.FormatRoles(roles) + return roles +} - // Then... - assert.Nil(t, err) - expectedFormattedOutput := "Total:0\n" - assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) +func TestRolesSummaryFormatterHasCorrectName(t *testing.T) { + formatter := NewRolesSummaryFormatter() + assert.Equal(t, formatter.GetName(), "summary") } -func TestSecretSummaryFormatterSingleDataReturnsCorrectly(t *testing.T) { +func TestRolesSummaryFormatterValidDataReturnsTotalCountTwo(t *testing.T) { // Given... - formatter := NewSecretSummaryFormatter() - description := "secret for system1" - secretName := "MYSECRET" - secret1 := createMockGalasaSecretWithDescription(secretName, description) - secrets := []galasaapi.GalasaSecret{secret1} + formatter := NewRolesSummaryFormatter() + roles := createTestRoles() // When... - actualFormattedOutput, err := formatter.FormatSecrets(secrets) + actualFormattedOutput, err := formatter.FormatRoles(roles) // Then... assert.Nil(t, err) - expectedFormattedOutput := - `name type last-updated(UTC) last-updated-by description -MYSECRET UsernamePassword 2024-01-01 10:00:00 dummy-username secret for system1 + expectedFormattedOutput := `name description +role1Name role1Description +role2Name role2Description -Total:1 +Total:2 ` assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) } -func TestSecretSummaryFormatterMultipleDataSeperatesWithNewLine(t *testing.T) { - // Given.. - formatter := NewSecretSummaryFormatter() - secrets := make([]galasaapi.GalasaSecret, 0) - - secret1Name := "SECRET1" - secret1Description := "my first secret" - secret2Name := "SECRET2" - secret2Description := "my second secret" - secret3Name := "SECRET3" - secret3Description := "my third secret" - - secret1 := createMockGalasaSecretWithDescription(secret1Name, secret1Description) - secret2 := createMockGalasaSecretWithDescription(secret2Name, secret2Description) - secret3 := createMockGalasaSecretWithDescription(secret3Name, secret3Description) - secrets = append(secrets, secret1, secret2, secret3) +func TestRolesSummaryFormatterNoDataReturnsTotalCountAllZeros(t *testing.T) { + // Given... + formatter := NewRolesSummaryFormatter() + roles := make([]galasaapi.RBACRole, 0) // When... - actualFormattedOutput, err := formatter.FormatSecrets(secrets) + actualFormattedOutput, err := formatter.FormatRoles(roles) // Then... assert.Nil(t, err) - expectedFormattedOutput := - `name type last-updated(UTC) last-updated-by description -SECRET1 UsernamePassword 2024-01-01 10:00:00 dummy-username my first secret -SECRET2 UsernamePassword 2024-01-01 10:00:00 dummy-username my second secret -SECRET3 UsernamePassword 2024-01-01 10:00:00 dummy-username my third secret - -Total:3 -` + expectedFormattedOutput := "Total:0\n" assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) } - -*/ diff --git a/pkg/rolesformatter/yamlFormatter_test.go b/pkg/rolesformatter/yamlFormatter_test.go index ae070af1..03aa0f04 100644 --- a/pkg/rolesformatter/yamlFormatter_test.go +++ b/pkg/rolesformatter/yamlFormatter_test.go @@ -5,88 +5,65 @@ */ package rolesformatter -/* import ( - "fmt" "testing" "github.com/galasa-dev/cli/pkg/galasaapi" "github.com/stretchr/testify/assert" ) -func createMockGalasaSecret(secretName string) galasaapi.GalasaSecret { - return createMockGalasaSecretWithDescription(secretName, "") -} - -func generateExpectedSecretYaml(secretName string) string { - return fmt.Sprintf( - `apiVersion: %s -kind: GalasaSecret -metadata: - name: %s - lastUpdatedTime: 2024-01-01T10:00:00Z - lastUpdatedBy: %s - encoding: %s - type: UsernamePassword -data: - username: %s - password: %s`, API_VERSION, secretName, DUMMY_USERNAME, DUMMY_ENCODING, DUMMY_USERNAME, DUMMY_PASSWORD) +func TestRolesYamlFormatterHasCorrectName(t *testing.T) { + formatter := NewRolesYamlFormatter() + assert.Equal(t, formatter.GetName(), "yaml") } -func TestSecretsYamlFormatterNoDataReturnsBlankString(t *testing.T) { +func TestRolesYamlFormatterValidData(t *testing.T) { // Given... - formatter := NewSecretYamlFormatter() - formattableSecret := make([]galasaapi.GalasaSecret, 0) - - // When... - actualFormattedOutput, err := formatter.FormatSecrets(formattableSecret) - - // Then... - assert.Nil(t, err) - expectedFormattedOutput := "" - assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) -} - -func TestSecretsYamlFormatterSingleDataReturnsCorrectly(t *testing.T) { - // Given.. - formatter := NewSecretYamlFormatter() - formattableSecrets := make([]galasaapi.GalasaSecret, 0) - secretName := "SECRET1" - secret1 := createMockGalasaSecret(secretName) - formattableSecrets = append(formattableSecrets, secret1) + formatter := NewRolesYamlFormatter() + roles := createTestRoles() // When... - actualFormattedOutput, err := formatter.FormatSecrets(formattableSecrets) + actualFormattedOutput, err := formatter.FormatRoles(roles) // Then... assert.Nil(t, err) - expectedFormattedOutput := generateExpectedSecretYaml(secretName) + "\n" + expectedFormattedOutput := `apiversion: galasa-dev/v1alpha1 +kind: GalasaRole +metadata: + id: role1Id + name: role1Name + description: role1Description + url: http://myHost/api/rbac/roles/role1Id +data: + actions: + - action1 + - action2 +--- +apiversion: galasa-dev/v1alpha1 +kind: GalasaRole +metadata: + id: role2Id + name: role2Name + description: role2Description + url: http://myHost/api/rbac/roles/role2Id +data: + actions: + - action1 + - action2 +` assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) } -func TestSecretsYamlFormatterMultipleDataSeperatesWithNewLine(t *testing.T) { - // For.. - formatter := NewSecretYamlFormatter() - formattableSecrets := make([]galasaapi.GalasaSecret, 0) - - secret1Name := "MYSECRET" - secret2Name := "MY-NEXT-SECRET" - secret1 := createMockGalasaSecret(secret1Name) - secret2 := createMockGalasaSecret(secret2Name) - formattableSecrets = append(formattableSecrets, secret1, secret2) +func TestRolesYamlFormatterNoDataReturnsTotalCountAllZeros(t *testing.T) { + // Given... + formatter := NewRolesYamlFormatter() + roles := make([]galasaapi.RBACRole, 0) // When... - actualFormattedOutput, err := formatter.FormatSecrets(formattableSecrets) + actualFormattedOutput, err := formatter.FormatRoles(roles) // Then... assert.Nil(t, err) - expectedSecret1Output := generateExpectedSecretYaml(secret1Name) - expectedSecret2Output := generateExpectedSecretYaml(secret2Name) - expectedFormattedOutput := fmt.Sprintf(`%s ---- -%s -`, expectedSecret1Output, expectedSecret2Output) + expectedFormattedOutput := "" assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) } - -*/ From adb5f1d576b6881c9fd561e946aa2c2d659e4c22 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:01:32 +0000 Subject: [PATCH 02/21] readme includes instructions on using the galasactl roles get command Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 6691af5d..81bcd89f 100644 --- a/README.md +++ b/README.md @@ -728,6 +728,39 @@ galasactl secrets delete --name SYSTEM1 For a complete list of supported parameters see [here](./docs/generated/galasactl_secrets_delete.md). +## roles get +To list the roles which are available on a Galasa service. + +Note: Roles are currently read-only and cannot be used in conjunction with the `galasactl resources apply -f` or simuilar commands at this time. + +### Examples +``` +> galasactl roles get +name description +admin Administrator access +deactivated User has no access +tester Test developer and runner + +Total:3 +``` + +To get a named role in yaml format +``` +>galasactl roles get --name admin --format yaml +apiversion: galasa-dev/v1alpha1 +kind: GalasaRole +metadata: + id: "2" + name: admin + description: Administrator access + url: http://prod1-galasa-dev.cicsk8s.hursley.ibm.com/rbac/roles/2 +data: + actions: + - GENERAL_API_ACCESS + - SECRETS_GET + - USER_ROLE_UPDATE_ANY +``` + ## Reference Material ### Syntax From 193ffc067325d3661a8ebdce3451947d3619a91d Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:08:39 +0000 Subject: [PATCH 03/21] upgrade some transitive dependencies to try to satisfy snyk scans Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- go.mod | 2 ++ go.sum | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/go.mod b/go.mod index be851525..243f3338 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,9 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.10.0 + golang.org/x/crypto v0.31.0 // indirect golang.org/x/image v0.23.0 + golang.org/x/text v0.21.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index a0945878..1a9cae2f 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -70,6 +72,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -78,6 +81,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= From 4c6a4b4507024839c460f72a7d95814298c6b87e Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Tue, 31 Dec 2024 16:54:55 +0000 Subject: [PATCH 04/21] corrections based on a code review Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- README.md | 2 +- pkg/cmd/roles.go | 4 ++-- pkg/roles/roles.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 81bcd89f..e70d38c1 100644 --- a/README.md +++ b/README.md @@ -731,7 +731,7 @@ For a complete list of supported parameters see [here](./docs/generated/galasact ## roles get To list the roles which are available on a Galasa service. -Note: Roles are currently read-only and cannot be used in conjunction with the `galasactl resources apply -f` or simuilar commands at this time. +Note: Roles are currently read-only and cannot be used in conjunction with the `galasactl resources apply -f` or similar commands at this time. ### Examples ``` diff --git a/pkg/cmd/roles.go b/pkg/cmd/roles.go index 232a37e1..f40a06dc 100644 --- a/pkg/cmd/roles.go +++ b/pkg/cmd/roles.go @@ -80,9 +80,9 @@ func addRolesNameFlag(cmd *cobra.Command, isMandatory bool, RolesCmdValues *Role flagName := "name" var description string if isMandatory { - description = "A mandatory flag that identifies the secret to be created or manipulated." + description = "A mandatory flag that identifies the role to be created or manipulated." } else { - description = "An optional flag that identifies the secret to be retrieved." + description = "An optional flag that identifies the role to be retrieved by name." } cmd.Flags().StringVar(&RolesCmdValues.name, flagName, "", description) diff --git a/pkg/roles/roles.go b/pkg/roles/roles.go index 6bb08f53..75de23e3 100644 --- a/pkg/roles/roles.go +++ b/pkg/roles/roles.go @@ -15,7 +15,7 @@ func validateRoleName(nameToValidate string) (string, error) { var err error name := strings.TrimSpace(nameToValidate) - if name == "" || strings.ContainsAny(name, " .\n\t") || !isLatin1(name) { + if name == "" || !usesValidRoleCharacters(name) { err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_ROLE_NAME) } return name, err @@ -23,7 +23,7 @@ func validateRoleName(nameToValidate string) (string, error) { // Checks if a given string contains only characters in the Latin-1 character set (codepoints 0-255), // returning true if so, and false otherwise -func isLatin1(str string) bool { +func usesValidRoleCharacters(str string) bool { isValidLatin1 := true for _, character := range str { if !((character >= 'a' && character <= 'z') || From fec8400246054958c0f3fb146996c3abae9b7f41 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Tue, 31 Dec 2024 16:58:11 +0000 Subject: [PATCH 05/21] improved markdown docs of the role command Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- docs/generated/galasactl_Roles_get.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generated/galasactl_Roles_get.md b/docs/generated/galasactl_Roles_get.md index 1ae3fb9e..611fec47 100644 --- a/docs/generated/galasactl_Roles_get.md +++ b/docs/generated/galasactl_Roles_get.md @@ -15,7 +15,7 @@ galasactl roles get [flags] ``` --format string the output format of the returned Roles. Supported formats are: 'summary', 'yaml'. (default "summary") -h, --help Displays the options for the 'roles get' command. - --name string An optional flag that identifies the secret to be retrieved. + --name string An optional flag that identifies the role to be retrieved by name. ``` ### Options inherited from parent commands From b04203518e948eda6118af5097a24f5525d2e5ad Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:20:49 +0000 Subject: [PATCH 06/21] apiVersion renders in yaml correctly, as generated code is corrected now Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .github/workflows/build.yml | 6 ++ .github/workflows/pr-build.yml | 6 ++ README.md | 2 +- build-locally.sh | 8 ++ fix-generated-code.sh | 101 ++++++++++++++++++ go.mod | 2 - go.sum | 4 - pkg/properties/propertiesGet_test.go | 68 ++++++------ .../summaryFormatter_test.go | 2 + pkg/propertiesformatter/yamlFormatter.go | 6 -- pkg/propertiesformatter/yamlFormatter_test.go | 14 +-- pkg/roles/rolesGet_test.go | 5 +- pkg/rolesformatter/yamlFormatter_test.go | 4 +- pkg/secretsformatter/yamlFormatter.go | 3 +- 14 files changed, 171 insertions(+), 60 deletions(-) create mode 100755 fix-generated-code.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 320c0c7b..3bf975c4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,6 +61,12 @@ jobs: run : | docker run --rm -v ${{ github.workspace }}:/var/workspace ghcr.io/galasa-dev/openapi:main java -jar /opt/openapi/openapi-generator-cli.jar generate -i /var/workspace/build/dependencies/openapi.yaml -g go -o /var/workspace/pkg/galasaapi --additional-properties=packageName=galasaapi --global-property=apiTests=false + # The generated code needs adjustment for various reasons. + - name: Fix generated code + run : | + chmod +x ./fix-generated-code.sh + ./fix-generated-code.sh + # The go.mod and go.sum are out of date, as they do not include the generated code so they are deleted here. They get re-generated when we compile. # Due to permissions, deleting the go.mod and go.sum must be done by the openapi image as the image generated those files. diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 70424103..a7b6f823 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -52,6 +52,12 @@ jobs: run : | docker run --rm -v ${{ github.workspace }}:/var/workspace ghcr.io/galasa-dev/openapi:main java -jar /opt/openapi/openapi-generator-cli.jar generate -i /var/workspace/build/dependencies/openapi.yaml -g go -o /var/workspace/pkg/galasaapi --additional-properties=packageName=galasaapi --global-property=apiTests=false + # The generated code needs adjustment for various reasons. + - name: Fix generated code + run : | + chmod +x ./fix-generated-code.sh + ./fix-generated-code.sh + # The go.mod and go.sum are out of date, as they do not include the generated code so they are deleted here. They get re-generated when we compile. # Due to permissions, deleting the go.mod and go.sum must be done by the openapi image as the image generated those files. diff --git a/README.md b/README.md index e70d38c1..2de0e3ac 100644 --- a/README.md +++ b/README.md @@ -747,7 +747,7 @@ Total:3 To get a named role in yaml format ``` >galasactl roles get --name admin --format yaml -apiversion: galasa-dev/v1alpha1 +apiVersion: galasa-dev/v1alpha1 kind: GalasaRole metadata: id: "2" diff --git a/build-locally.sh b/build-locally.sh index 4ed13eec..90e0f203 100755 --- a/build-locally.sh +++ b/build-locally.sh @@ -181,6 +181,14 @@ function generate_rest_client { rc=$? ; if [[ "${rc}" != "0" ]]; then cat build/generate-log.txt ; error "Failed to generate II the code from the yaml file. rc=${rc}" ; exit 1 ; fi rm -f build/generate-log.txt success "Code generation part II - OK" + + #-------------------------------------------------------------------------- + # Invoke the generator again with different parameters + h2 "Generate the openapi client go code... part III - fixing it up." + ./fix-generated-code.sh 2>&1 > build/generate-fix-log.txt + rc=$? ; if [[ "${rc}" != "0" ]]; then cat build/generate-fix-log.txt ; error "Failed to generate III the code. (Fixing it) rc=${rc}" ; exit 1 ; fi + rm -f build/generate-fix-log.txt + success "Code generation part III - OK" } #-------------------------------------------------------------------------- diff --git a/fix-generated-code.sh b/fix-generated-code.sh new file mode 100755 index 00000000..f6bf60b8 --- /dev/null +++ b/fix-generated-code.sh @@ -0,0 +1,101 @@ +#! /usr/bin/env bash + +# +# Copyright contributors to the Galasa project +# +# SPDX-License-Identifier: EPL-2.0 +# + +#----------------------------------------------------------------------------------------- +# +# Objectives: Fix anything we need to in the generated code. +# +#----------------------------------------------------------------------------------------- + +# Where is this script executing from ? +BASEDIR=$(dirname "$0");pushd $BASEDIR 2>&1 >> /dev/null ;BASEDIR=$(pwd);popd 2>&1 >> /dev/null +# echo "Running from directory ${BASEDIR}" +export ORIGINAL_DIR=$(pwd) +# cd "${BASEDIR}" + +#----------------------------------------------------------------------------------------- +# +# Set Colors +# +#----------------------------------------------------------------------------------------- +bold=$(tput bold) +underline=$(tput sgr 0 1) +reset=$(tput sgr0) +red=$(tput setaf 1) +green=$(tput setaf 76) +white=$(tput setaf 7) +tan=$(tput setaf 202) +blue=$(tput setaf 25) + +#----------------------------------------------------------------------------------------- +# +# Headers and Logging +# +#----------------------------------------------------------------------------------------- +underline() { printf "${underline}${bold}%s${reset}\n" "$@" ;} +h1() { printf "\n${underline}${bold}${blue}%s${reset}\n" "$@" ;} +h2() { printf "\n${underline}${bold}${white}%s${reset}\n" "$@" ;} +debug() { printf "${white}%s${reset}\n" "$@" ;} +info() { printf "${white}➜ %s${reset}\n" "$@" ;} +success() { printf "${green}✔ %s${reset}\n" "$@" ;} +error() { printf "${red}✖ %s${reset}\n" "$@" ;} +warn() { printf "${tan}➜ %s${reset}\n" "$@" ;} +bold() { printf "${bold}%s${reset}\n" "$@" ;} +note() { printf "\n${underline}${bold}${blue}Note:${reset} ${blue}%s${reset}\n" "$@" ;} + +mkdir -p temp + +# Explanation: +# ------------ +# We have a bunch of model files generated. +# They all have this inside: for example: +# +# ApiVersion *string `json:"apiVersion,omitempty"` +# Kind *string `json:"kind,omitempty"` +# +# This is OK, but when rendering to yaml all the names of properties get screwed up. +# eg: the `ApiVersion` field is rendered as `apiVersion` in json (good) but in yaml it renders as `apiversion` (bad). +# ... but we want them to contain the same property names for json or yaml. +# +# So we need to add the yaml annotations to make it look like this: +# +# ApiVersion *string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` +# Kind *string `json:"kind,omitempty" yaml:"kind,omitempty"` +# +# Then the `ApiVersion` field can render to `apiVersion` in the yaml just as it does in the json. To be consistent. +# +cd pkg/galasaapi +rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed to change folders into the generated code" ; exit 1; fi + +# List all the model files we want to run the blanket transform over... +ls model*.go > $BASEDIR/temp/file-list.txt +rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed to list generated model files" ; exit 1; fi + +while IFS= read -r file_to_process; do + info "Processing file: ${file_to_process}" + + # Note the following about sed: + # (.*) collects the group. The name of the property as json will render it. + # \1 repeats that group in the substitution. So we can use it twice, once to repeat the json annotation, and once for the yaml one. + # eg: In the following example, the string "apiVersion,omitempty" is collected as the first group... so... + # + # ApiVersion *string `json:"apiVersion,omitempty"` + # + # ...will become... + # + # ApiVersion *string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + # + cat ${file_to_process} | sed "s/json:\(.*\)\"\`/json:\1\" yaml:\1\"\`/g" > ${BASEDIR}/temp/${file_to_process}.temp + rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed to substitute yaml serialisation annotation into generated model file ${file_to_process}" ; exit 1; fi + + # Copy the transformed version over the top of the original, leaving a copy in the temp folder to diagnose problems with. + cp ${BASEDIR}/temp/${file_to_process}.temp ${file_to_process} + rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed to copy fixed code over the generated original code at ${file_to_process}" ; exit 1; fi + +done < $BASEDIR/temp/file-list.txt +rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed inside while loop." ; exit 1; fi diff --git a/go.mod b/go.mod index 243f3338..be851525 100644 --- a/go.mod +++ b/go.mod @@ -9,9 +9,7 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.10.0 - golang.org/x/crypto v0.31.0 // indirect golang.org/x/image v0.23.0 - golang.org/x/text v0.21.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 1a9cae2f..a0945878 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -72,7 +70,6 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -81,7 +78,6 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/pkg/properties/propertiesGet_test.go b/pkg/properties/propertiesGet_test.go index b464fd03..9dd4e3b0 100644 --- a/pkg/properties/propertiesGet_test.go +++ b/pkg/properties/propertiesGet_test.go @@ -73,8 +73,8 @@ func CheckName(name string) (string, int) { case "property0": namespaceProperties = `[ { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "property0" @@ -89,8 +89,8 @@ func CheckName(name string) (string, int) { case "emptyValueName": //property name does not exist namespaceProperties = `[ { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "emptyValueName" @@ -112,8 +112,8 @@ func checkQueryParameters(prefixParameter string, suffixParameter string, infixP if infixParameter == "anInfix" { //for a single infix namespaceProperties = `[ { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "aPrefix.anInfix.property.aSuffix" @@ -127,8 +127,8 @@ func checkQueryParameters(prefixParameter string, suffixParameter string, infixP } else { //no infix namespaceProperties = `[ { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "aPrefix.property.aSuffix" @@ -146,8 +146,8 @@ func checkQueryParameters(prefixParameter string, suffixParameter string, infixP if infixParameter == "anInfix" { namespaceProperties = `[ { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "property.anInfix.aSuffix" @@ -161,8 +161,8 @@ func checkQueryParameters(prefixParameter string, suffixParameter string, infixP } else { namespaceProperties = `[ { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "property.aSuffix" @@ -180,8 +180,8 @@ func checkQueryParameters(prefixParameter string, suffixParameter string, infixP if infixParameter == "anInfix" { namespaceProperties = `[ { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "aPrefix.anInfix.property" @@ -194,8 +194,8 @@ func checkQueryParameters(prefixParameter string, suffixParameter string, infixP } else { namespaceProperties = `[ { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "aPrefix.property" @@ -222,8 +222,8 @@ func checkQueryParameters(prefixParameter string, suffixParameter string, infixP if infixParameter == "anInfix" { namespaceProperties = `[ { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "extra.anInfix.extra" @@ -249,8 +249,8 @@ func CheckNamespace(namespace string) (int, string) { case "validnamespace": namespaceProperties = `[ { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "property0" @@ -260,8 +260,8 @@ func CheckNamespace(namespace string) (int, string) { } }, { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "property1" @@ -271,8 +271,8 @@ func CheckNamespace(namespace string) (int, string) { } }, { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "property2" @@ -282,8 +282,8 @@ func CheckNamespace(namespace string) (int, string) { } }, { - "apiVersion": null, - "kind": null, + "apiVersion": "myApiVersion", + "kind": "GalasaProperty", "metadata": { "namespace": "validnamespace", "name": "property3" @@ -912,32 +912,32 @@ func TestValidNamespaceYamlFormatReturnsOk(t *testing.T) { apiClient := api.InitialiseAPI(apiServerUrl) - expectedOutput := `apiVersion: null -kind: null + expectedOutput := `apiVersion: myApiVersion +kind: GalasaProperty metadata: namespace: validnamespace name: property0 data: value: value0 --- -apiVersion: null -kind: null +apiVersion: myApiVersion +kind: GalasaProperty metadata: namespace: validnamespace name: property1 data: value: value1 --- -apiVersion: null -kind: null +apiVersion: myApiVersion +kind: GalasaProperty metadata: namespace: validnamespace name: property2 data: value: value2 --- -apiVersion: null -kind: null +apiVersion: myApiVersion +kind: GalasaProperty metadata: namespace: validnamespace name: property3 diff --git a/pkg/propertiesformatter/summaryFormatter_test.go b/pkg/propertiesformatter/summaryFormatter_test.go index a3dbec69..311b1a6b 100644 --- a/pkg/propertiesformatter/summaryFormatter_test.go +++ b/pkg/propertiesformatter/summaryFormatter_test.go @@ -18,6 +18,8 @@ import ( func CreateMockGalasaProperty(namespace string, name string, value string) *galasaapi.GalasaProperty { var property = galasaapi.NewGalasaProperty() + property.SetApiVersion("myApiVersion") + property.SetKind("GalasaProperty") metadata := galasaapi.NewGalasaPropertyMetadata() metadata.SetNamespace(namespace) metadata.SetName(name) diff --git a/pkg/propertiesformatter/yamlFormatter.go b/pkg/propertiesformatter/yamlFormatter.go index 9fc8c04c..80ba34ed 100644 --- a/pkg/propertiesformatter/yamlFormatter.go +++ b/pkg/propertiesformatter/yamlFormatter.go @@ -46,12 +46,6 @@ func (*PropertyYamlFormatter) FormatProperties(cpsProperties []galasaapi.GalasaP yamlRepresentationBytes, err = yaml.Marshal(property) if err == nil { yamlStr := string(yamlRepresentationBytes) - - // The generated bean serialises in json as 'apiVersion' which is correct. In yaml it serialises as 'apiversion' (incorrect) - // So this is a hack to correct that failure. - // Note: This will corrupt any value string which also has 'apiversion' inside it ! - // TODO: The fix is to change the bean and add a 'yaml' annotation so it gets rendered correctly. Golang has yaml annotations, but does the generator support them ? - yamlStr = strings.ReplaceAll(yamlStr, "apiversion", "apiVersion") propertyString += yamlStr } diff --git a/pkg/propertiesformatter/yamlFormatter_test.go b/pkg/propertiesformatter/yamlFormatter_test.go index 66abef7e..e39ddcf2 100644 --- a/pkg/propertiesformatter/yamlFormatter_test.go +++ b/pkg/propertiesformatter/yamlFormatter_test.go @@ -40,8 +40,8 @@ func TestPropertiesYamlFormatterSingleDataReturnsCorrectly(t *testing.T) { // Then... assert.Nil(t, err) - expectedFormattedOutput := `apiVersion: null -kind: null + expectedFormattedOutput := `apiVersion: myApiVersion +kind: GalasaProperty metadata: namespace: namespace name: name1 @@ -65,16 +65,16 @@ func TestPropertiesYamlFormatterMultipleDataSeperatesWithNewLine(t *testing.T) { // Then... assert.Nil(t, err) - expectedFormattedOutput := `apiVersion: null -kind: null + expectedFormattedOutput := `apiVersion: myApiVersion +kind: GalasaProperty metadata: namespace: namespace name: name1 data: value: value1 --- -apiVersion: null -kind: null +apiVersion: myApiVersion +kind: GalasaProperty metadata: namespace: namespace name: name2 @@ -82,4 +82,4 @@ data: value: value2 ` assert.Equal(t, expectedFormattedOutput, actualFormattedOutput) -} \ No newline at end of file +} diff --git a/pkg/roles/rolesGet_test.go b/pkg/roles/rolesGet_test.go index 1ecd57dd..33cc1e1f 100644 --- a/pkg/roles/rolesGet_test.go +++ b/pkg/roles/rolesGet_test.go @@ -32,6 +32,7 @@ func createTestGalasaRole(id string, name string, description string) galasaapi. if description != "" { metadata.SetDescription(description) } + metadata.SetUrl("https://myhost:myport/rbac/roles/" + id) data := *galasaapi.NewRBACRoleData() actionStrings := make([]string, 2) @@ -199,13 +200,13 @@ func TestCanGetARoleByIdWhenRoleExistsFindsItOkInYamlFormat(t *testing.T) { // Then... expectedOutput := - `apiversion: galasa-dev/v1alpha1 + `apiVersion: galasa-dev/v1alpha1 kind: GalasaRole metadata: id: role1 name: role1Name description: role1Description - url: null + url: https://myhost:myport/rbac/roles/role1 data: actions: - action1 diff --git a/pkg/rolesformatter/yamlFormatter_test.go b/pkg/rolesformatter/yamlFormatter_test.go index 03aa0f04..71c5d545 100644 --- a/pkg/rolesformatter/yamlFormatter_test.go +++ b/pkg/rolesformatter/yamlFormatter_test.go @@ -27,7 +27,7 @@ func TestRolesYamlFormatterValidData(t *testing.T) { // Then... assert.Nil(t, err) - expectedFormattedOutput := `apiversion: galasa-dev/v1alpha1 + expectedFormattedOutput := `apiVersion: galasa-dev/v1alpha1 kind: GalasaRole metadata: id: role1Id @@ -39,7 +39,7 @@ data: - action1 - action2 --- -apiversion: galasa-dev/v1alpha1 +apiVersion: galasa-dev/v1alpha1 kind: GalasaRole metadata: id: role2Id diff --git a/pkg/secretsformatter/yamlFormatter.go b/pkg/secretsformatter/yamlFormatter.go index 2f5865f7..8f02624d 100644 --- a/pkg/secretsformatter/yamlFormatter.go +++ b/pkg/secretsformatter/yamlFormatter.go @@ -32,7 +32,6 @@ func (*SecretYamlFormatter) FormatSecrets(secrets []galasaapi.GalasaSecret) (str buff := strings.Builder{} for index, secret := range secrets { - galasaSecret := NewGalasaSecret(secret) secretString := "" if index > 0 { @@ -40,7 +39,7 @@ func (*SecretYamlFormatter) FormatSecrets(secrets []galasaapi.GalasaSecret) (str } var yamlRepresentationBytes []byte - yamlRepresentationBytes, err = yaml.Marshal(galasaSecret) + yamlRepresentationBytes, err = yaml.Marshal(secret) if err == nil { yamlStr := string(yamlRepresentationBytes) secretString += yamlStr From 219eeae885339af66e15eb606de356cde32c1ae2 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:28:59 +0000 Subject: [PATCH 07/21] trying to allow the fix-generated-code.sh to work against read-only generated source files. Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- fix-generated-code.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fix-generated-code.sh b/fix-generated-code.sh index f6bf60b8..d0540b40 100755 --- a/fix-generated-code.sh +++ b/fix-generated-code.sh @@ -93,6 +93,10 @@ while IFS= read -r file_to_process; do cat ${file_to_process} | sed "s/json:\(.*\)\"\`/json:\1\" yaml:\1\"\`/g" > ${BASEDIR}/temp/${file_to_process}.temp rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed to substitute yaml serialisation annotation into generated model file ${file_to_process}" ; exit 1; fi + # Make the source file read-write, just in case we can't write to it currently. + chmod +w ${file_to_process} + rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed to make file ${file_to_process} writeable." ; exit 1; fi + # Copy the transformed version over the top of the original, leaving a copy in the temp folder to diagnose problems with. cp ${BASEDIR}/temp/${file_to_process}.temp ${file_to_process} rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed to copy fixed code over the generated original code at ${file_to_process}" ; exit 1; fi From 71b3d8ddace54a8cebb932b9530372787a87ca05 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:31:55 +0000 Subject: [PATCH 08/21] trying to allow the fix-generated-code.sh to work against read-only generated source files. Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .github/workflows/build.yml | 1 + fix-generated-code.sh | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3bf975c4..b8a675bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,6 +65,7 @@ jobs: - name: Fix generated code run : | chmod +x ./fix-generated-code.sh + chmod +w /var/workspace/pkg/galasaapi/* ./fix-generated-code.sh # The go.mod and go.sum are out of date, as they do not include the generated code so they are deleted here. They get re-generated when we compile. diff --git a/fix-generated-code.sh b/fix-generated-code.sh index d0540b40..f6bf60b8 100755 --- a/fix-generated-code.sh +++ b/fix-generated-code.sh @@ -93,10 +93,6 @@ while IFS= read -r file_to_process; do cat ${file_to_process} | sed "s/json:\(.*\)\"\`/json:\1\" yaml:\1\"\`/g" > ${BASEDIR}/temp/${file_to_process}.temp rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed to substitute yaml serialisation annotation into generated model file ${file_to_process}" ; exit 1; fi - # Make the source file read-write, just in case we can't write to it currently. - chmod +w ${file_to_process} - rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed to make file ${file_to_process} writeable." ; exit 1; fi - # Copy the transformed version over the top of the original, leaving a copy in the temp folder to diagnose problems with. cp ${BASEDIR}/temp/${file_to_process}.temp ${file_to_process} rc=$? ; if [[ "$rc" != "0" ]]; then error "Failed to copy fixed code over the generated original code at ${file_to_process}" ; exit 1; fi From 20add559fa2d3ac303c6426d4ab7122719ec6427 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:36:13 +0000 Subject: [PATCH 09/21] trying to allow the fix-generated-code.sh to work against read-only generated source files. Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b8a675bb..5d3d0869 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,7 +65,8 @@ jobs: - name: Fix generated code run : | chmod +x ./fix-generated-code.sh - chmod +w /var/workspace/pkg/galasaapi/* + chmod 0666 /var/workspace/pkg/galasaapi/* + ls -l /var/workspace/pkg/galasaapi/* ./fix-generated-code.sh # The go.mod and go.sum are out of date, as they do not include the generated code so they are deleted here. They get re-generated when we compile. From e7a91240aa1583847a7ba42191098ef126ee0a40 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:41:06 +0000 Subject: [PATCH 10/21] trying to allow the fix-generated-code.sh to work against read-only generated source files. Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d3d0869..5ca9e24f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,6 +64,7 @@ jobs: # The generated code needs adjustment for various reasons. - name: Fix generated code run : | + whoami chmod +x ./fix-generated-code.sh chmod 0666 /var/workspace/pkg/galasaapi/* ls -l /var/workspace/pkg/galasaapi/* From 06a3fa1262c0bd59ab88e125a8d09155ca0aea79 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:43:08 +0000 Subject: [PATCH 11/21] trying to allow the fix-generated-code.sh to work against read-only generated source files. Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .github/workflows/pr-build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index a7b6f823..b942f4e4 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -55,9 +55,12 @@ jobs: # The generated code needs adjustment for various reasons. - name: Fix generated code run : | + whoami chmod +x ./fix-generated-code.sh + chmod 0666 /var/workspace/pkg/galasaapi/* + ls -l /var/workspace/pkg/galasaapi/* ./fix-generated-code.sh - + # The go.mod and go.sum are out of date, as they do not include the generated code so they are deleted here. They get re-generated when we compile. # Due to permissions, deleting the go.mod and go.sum must be done by the openapi image as the image generated those files. From d0dc6379d821a97b4ed320c472715935b5bf5c7f Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 10:19:02 +0000 Subject: [PATCH 12/21] trying to allow the fix-generated-code.sh to work against read-only generated source files. Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .github/workflows/pr-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index b942f4e4..cd6d9301 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -57,10 +57,10 @@ jobs: run : | whoami chmod +x ./fix-generated-code.sh - chmod 0666 /var/workspace/pkg/galasaapi/* - ls -l /var/workspace/pkg/galasaapi/* + chmod 0666 ./pkg/galasaapi/* + ls -l ./pkg/galasaapi/* ./fix-generated-code.sh - + # The go.mod and go.sum are out of date, as they do not include the generated code so they are deleted here. They get re-generated when we compile. # Due to permissions, deleting the go.mod and go.sum must be done by the openapi image as the image generated those files. From c7e7e55945719e044efedd3da413ba0cac75e9b6 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 10:21:25 +0000 Subject: [PATCH 13/21] trying to allow the fix-generated-code.sh to work against read-only generated source files. Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .github/workflows/pr-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index cd6d9301..414fe657 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -56,9 +56,9 @@ jobs: - name: Fix generated code run : | whoami + ls -l ./pkg/galasaapi/* chmod +x ./fix-generated-code.sh chmod 0666 ./pkg/galasaapi/* - ls -l ./pkg/galasaapi/* ./fix-generated-code.sh # The go.mod and go.sum are out of date, as they do not include the generated code so they are deleted here. They get re-generated when we compile. From 8ff13bff3edff352b02b1ac2d8038da324b5c209 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:26:51 +0000 Subject: [PATCH 14/21] trying to get the build to run the generator as non-root user Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/pr-build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ca9e24f..64a64a27 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,7 +59,7 @@ jobs: # Generate client code so galasactl can communicate with the API server. - name: Generate Go client code using openapi.yaml run : | - docker run --rm -v ${{ github.workspace }}:/var/workspace ghcr.io/galasa-dev/openapi:main java -jar /opt/openapi/openapi-generator-cli.jar generate -i /var/workspace/build/dependencies/openapi.yaml -g go -o /var/workspace/pkg/galasaapi --additional-properties=packageName=galasaapi --global-property=apiTests=false + docker run --rm --user $(id -u):$(id -g) -v ${{ github.workspace }}:/var/workspace ghcr.io/galasa-dev/openapi:main java -jar /opt/openapi/openapi-generator-cli.jar generate -i /var/workspace/build/dependencies/openapi.yaml -g go -o /var/workspace/pkg/galasaapi --additional-properties=packageName=galasaapi --global-property=apiTests=false # The generated code needs adjustment for various reasons. - name: Fix generated code diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 414fe657..c8828f9a 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -50,7 +50,7 @@ jobs: # Generate client code so galasactl can communicate with the API server. - name: Generate Go client code using openapi.yaml run : | - docker run --rm -v ${{ github.workspace }}:/var/workspace ghcr.io/galasa-dev/openapi:main java -jar /opt/openapi/openapi-generator-cli.jar generate -i /var/workspace/build/dependencies/openapi.yaml -g go -o /var/workspace/pkg/galasaapi --additional-properties=packageName=galasaapi --global-property=apiTests=false + docker run --rm --user $(id -u):$(id -g) -v ${{ github.workspace }}:/var/workspace ghcr.io/galasa-dev/openapi:main java -jar /opt/openapi/openapi-generator-cli.jar generate -i /var/workspace/build/dependencies/openapi.yaml -g go -o /var/workspace/pkg/galasaapi --additional-properties=packageName=galasaapi --global-property=apiTests=false # The generated code needs adjustment for various reasons. - name: Fix generated code From 81716fa7ca51cf88ff0715c93acd64b928578a61 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:29:28 +0000 Subject: [PATCH 15/21] trying to get the build to run the generator as non-root user Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .github/workflows/build.yml | 3 --- .github/workflows/pr-build.yml | 3 --- 2 files changed, 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64a64a27..563602dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,10 +64,7 @@ jobs: # The generated code needs adjustment for various reasons. - name: Fix generated code run : | - whoami chmod +x ./fix-generated-code.sh - chmod 0666 /var/workspace/pkg/galasaapi/* - ls -l /var/workspace/pkg/galasaapi/* ./fix-generated-code.sh # The go.mod and go.sum are out of date, as they do not include the generated code so they are deleted here. They get re-generated when we compile. diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index c8828f9a..d1c197b2 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -55,10 +55,7 @@ jobs: # The generated code needs adjustment for various reasons. - name: Fix generated code run : | - whoami - ls -l ./pkg/galasaapi/* chmod +x ./fix-generated-code.sh - chmod 0666 ./pkg/galasaapi/* ./fix-generated-code.sh # The go.mod and go.sum are out of date, as they do not include the generated code so they are deleted here. They get re-generated when we compile. From d0d9d608ad3784f0c6669f02db1b52bf24581ee5 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:07:21 +0000 Subject: [PATCH 16/21] empty commit to test build process Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> From bb0e85f8fa28fb166f3c4a9d7a3cef1d54288dfe Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:21:00 +0000 Subject: [PATCH 17/21] empty commit to test build process Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> From 3b75092e2b83300d101960076ccd1f3603c4c08a Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:23:06 +0000 Subject: [PATCH 18/21] empty commit to test build process Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> From 17094a9ee489a7d10dfe70ca58c8b14d860dee8a Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:07:07 +0000 Subject: [PATCH 19/21] fix script should run in sh so it works on busybox in tekton Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- fix-generated-code.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fix-generated-code.sh b/fix-generated-code.sh index f6bf60b8..fe8a45ec 100755 --- a/fix-generated-code.sh +++ b/fix-generated-code.sh @@ -1,4 +1,4 @@ -#! /usr/bin/env bash +#! /usr/bin/env sh # # Copyright contributors to the Galasa project From ff7f905bd1d1d672c133b66149a6001cbdd6ac83 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:37:20 +0000 Subject: [PATCH 20/21] trying to solve things snyk is complaining about Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- build-locally.sh | 2 +- go.mod | 3 +++ go.sum | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/build-locally.sh b/build-locally.sh index 90e0f203..ea9557fb 100755 --- a/build-locally.sh +++ b/build-locally.sh @@ -640,7 +640,7 @@ function check_secrets { clean download_dependencies generate_rest_client -go_mod_tidy +# go_mod_tidy - don't tidy the go.mod as it gets rid of transitive/indirect dependencies. build_executables diff --git a/go.mod b/go.mod index be851525..2e9f8a0c 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,10 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.10.0 + golang.org/x/crypto v0.31.0 // indirect golang.org/x/image v0.23.0 + golang.org/x/net v0.26.0 // indirect + golang.org/x/term v0.27.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index a0945878..08c17572 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,9 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -53,6 +56,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -70,6 +75,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -78,6 +85,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -86,6 +95,7 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 5331f54961dab854ec0581f91df4d2922ab58343 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:49:51 +0000 Subject: [PATCH 21/21] remove unnecessary secrets bean used to format yaml correctly Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .secrets.baseline | 10 ----- pkg/secretsformatter/GalasaSecret.go | 65 ---------------------------- 2 files changed, 75 deletions(-) delete mode 100644 pkg/secretsformatter/GalasaSecret.go diff --git a/.secrets.baseline b/.secrets.baseline index 42b60f56..694da9de 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -206,16 +206,6 @@ "verified_result": null } ], - "pkg/secretsformatter/GalasaSecret.go": [ - { - "hashed_secret": "1949c4c92eb313637b3b6f654f5cce42df0dde88", - "is_secret": false, - "is_verified": false, - "line_number": 62, - "type": "Secret Keyword", - "verified_result": null - } - ], "pkg/secretsformatter/summaryFormatter.go": [ { "hashed_secret": "4d55af37dbbb6a42088d917caa1ca25428ec42c9", diff --git a/pkg/secretsformatter/GalasaSecret.go b/pkg/secretsformatter/GalasaSecret.go deleted file mode 100644 index 37c48674..00000000 --- a/pkg/secretsformatter/GalasaSecret.go +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright contributors to the Galasa project - * - * SPDX-License-Identifier: EPL-2.0 - */ -package secretsformatter - -import ( - "time" - - "github.com/galasa-dev/cli/pkg/galasaapi" -) - -// The auto-generated OpenAPI structs don't include `yaml` annotations, which causes -// issues when it comes to marshalling data into GalasaSecret structs in order to display -// secrets in YAML format. This is a manually-maintained struct that includes `yaml` annotations. -type GalasaSecret struct { - ApiVersion *string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` - Kind *string `json:"kind,omitempty" yaml:"kind,omitempty"` - Metadata *GalasaSecretMetadata `json:"metadata,omitempty" yaml:"metadata,omitempty"` - Data *GalasaSecretData `json:"data,omitempty" yaml:"data,omitempty"` -} - -type GalasaSecretMetadata struct { - Name *string `json:"name,omitempty" yaml:"name,omitempty"` - Description *string `json:"description,omitempty" yaml:"description,omitempty"` - LastUpdatedTime *time.Time `json:"lastUpdatedTime,omitempty" yaml:"lastUpdatedTime,omitempty"` - LastUpdatedBy *string `json:"lastUpdatedBy,omitempty" yaml:"lastUpdatedBy,omitempty"` - Encoding *string `json:"encoding,omitempty" yaml:"encoding,omitempty"` - Type *galasaapi.GalasaSecretType `json:"type,omitempty" yaml:"type,omitempty"` -} - -type GalasaSecretData struct { - Username *string `json:"username,omitempty" yaml:"username,omitempty"` - Password *string `json:"password,omitempty" yaml:"password,omitempty"` - Token *string `json:"token,omitempty" yaml:"token,omitempty"` -} - -func NewGalasaSecret(secret galasaapi.GalasaSecret) *GalasaSecret { - return &GalasaSecret{ - ApiVersion: secret.ApiVersion, - Kind: secret.Kind, - Metadata: NewGalasaSecretMetadata(secret.Metadata), - Data: NewGalasaSecretData(secret.Data), - } -} - -func NewGalasaSecretMetadata(metadata *galasaapi.GalasaSecretMetadata) *GalasaSecretMetadata { - return &GalasaSecretMetadata{ - Name: metadata.Name, - Description: metadata.Description, - LastUpdatedTime: metadata.LastUpdatedTime, - LastUpdatedBy: metadata.LastUpdatedBy, - Encoding: metadata.Encoding, - Type: metadata.Type, - } -} - -func NewGalasaSecretData(data *galasaapi.GalasaSecretData) *GalasaSecretData { - return &GalasaSecretData{ - Username: data.Username, - Password: data.Password, - Token: data.Token, - } -} \ No newline at end of file