Skip to content

Commit

Permalink
Add project secret create to sem create secret (#228)
Browse files Browse the repository at this point in the history
* Add project secret create to sem create secret

* Introduce test for create project secret
  • Loading branch information
hamir-suspect authored Apr 22, 2024
1 parent f37692f commit 587ba17
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 33 deletions.
141 changes: 108 additions & 33 deletions cmd/create_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func NewCreateSecretCmd() *cobra.Command {
cmd := &cobra.Command{}

cmd.Use = "secret [NAME]"
cmd.Short = "Create a secret."
cmd.Short = "Create a secret. If project is specified (via -p or -i), create a project secret."
cmd.Long = ``
cmd.Aliases = []string{"secrets"}
cmd.Args = cobra.ExactArgs(1)
Expand All @@ -34,59 +34,134 @@ func NewCreateSecretCmd() *cobra.Command {
"Environment Variables",
)

cmd.Flags().StringP("project-name", "p", "",
"project name; if specified will edit project level secret, otherwise organization secret")
cmd.Flags().StringP("project-id", "i", "",
"project id; if specified will edit project level secret, otherwise organization secret")
cmd.MarkFlagsMutuallyExclusive("project-name", "project-id")

cmd.Run = func(cmd *cobra.Command, args []string) {
projectID := GetProjectID(cmd)

name := args[0]

fileFlags, err := cmd.Flags().GetStringArray("file")
utils.Check(err)
envFlags, err := cmd.Flags().GetStringArray("env")
utils.Check(err)

if projectID == "" {
files := parseSecretFiles(fileFlags)
envVars := parseSecretEnvVars(envFlags)

secret := models.NewSecretV1Beta(name, envVars, files)

c := client.NewSecretV1BetaApi()

_, err = c.CreateSecret(&secret)

var files []models.SecretV1BetaFile
for _, fileFlag := range fileFlags {
matchFormat, err := regexp.MatchString(`^[^: ]+:[^: ]+$`, fileFlag)
utils.Check(err)

if !matchFormat {
utils.Fail("The format of --file flag must be: <local-path>:<semaphore-path>")
}
fmt.Printf("Secret '%s' created.\n", secret.Metadata.Name)
} else {
files := parseProjectSecretFiles(fileFlags)
envVars := parseProjectSecretEnvVars(envFlags)

flagPaths := strings.Split(fileFlag, ":")
secret := models.NewProjectSecretV1(name, envVars, files)

file := models.SecretV1BetaFile{}
file.Path = flagPaths[1]
file.Content = encodeFromFileAt(flagPaths[0])
files = append(files, file)
}
c := client.NewProjectSecretV1Api(projectID)

envFlags, err := cmd.Flags().GetStringArray("env")
utils.Check(err)
_, err = c.CreateSecret(&secret)

var envVars []models.SecretV1BetaEnvVar
for _, envFlag := range envFlags {
matchFormat, err := regexp.MatchString(`^.+=.+$`, envFlag)
utils.Check(err)

if !matchFormat {
utils.Fail("The format of -e flag must be: <NAME>=<VALUE>")
}
fmt.Printf("Project secret '%s' created.\n", secret.Metadata.Name)
}
}

parts := strings.SplitN(envFlag, "=", 2)
return cmd
}

envVars = append(envVars, models.SecretV1BetaEnvVar{
Name: parts[0],
Value: parts[1],
})
}
func parseProjectSecretFiles(fileFlags []string) []models.ProjectSecretV1File {
var files []models.ProjectSecretV1File

secret := models.NewSecretV1Beta(name, envVars, files)
for _, fileFlag := range fileFlags {
p, c := parseFile(fileFlag)

c := client.NewSecretV1BetaApi()
var file models.ProjectSecretV1File
file.Path = p
file.Content = encodeFromFileAt(c)
files = append(files, file)
}
return files
}

_, err = c.CreateSecret(&secret)
func parseSecretFiles(fileFlags []string) []models.SecretV1BetaFile {
var files []models.SecretV1BetaFile

utils.Check(err)
for _, fileFlag := range fileFlags {
p, c := parseFile(fileFlag)

fmt.Printf("Secret '%s' created.\n", secret.Metadata.Name)
var file models.SecretV1BetaFile
file.Path = p
file.Content = encodeFromFileAt(c)
files = append(files, file)
}
return files
}

return cmd
func parseFile(fileFlag string) (path, content string) {
matchFormat, err := regexp.MatchString(`^[^: ]+:[^: ]+$`, fileFlag)
utils.Check(err)

if !matchFormat {
utils.Fail("The format of --file flag must be: <local-path>:<semaphore-path>")
}

flagPaths := strings.Split(fileFlag, ":")

return flagPaths[1], flagPaths[0]
}

func parseProjectSecretEnvVars(envFlags []string) []models.ProjectSecretV1EnvVar {
var envVars []models.ProjectSecretV1EnvVar

for _, envFlag := range envFlags {
n, v := parseEnvVar(envFlag)

var envVar models.ProjectSecretV1EnvVar
envVar.Name = n
envVar.Value = v
envVars = append(envVars, envVar)
}

return envVars
}

func parseSecretEnvVars(envFlags []string) []models.SecretV1BetaEnvVar {
var envVars []models.SecretV1BetaEnvVar

for _, envFlag := range envFlags {
n, v := parseEnvVar(envFlag)

var envVar models.SecretV1BetaEnvVar
envVar.Name = n
envVar.Value = v
envVars = append(envVars, envVar)
}

return envVars
}

func parseEnvVar(envFlag string) (name, value string) {
matchFormat, err := regexp.MatchString(`^.+=.+$`, envFlag)
utils.Check(err)

if !matchFormat {
utils.Fail("The format of -e flag must be: <NAME>=<VALUE>")
}

parts := strings.SplitN(envFlag, "=", 2)

return parts[0], parts[1]
}
56 changes: 56 additions & 0 deletions cmd/create_secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,59 @@ func Test__CreateSecret__WithSubcommand__Response200(t *testing.T) {

assert.Equal(t, received, expected)
}

func Test__CreateSecret__WithProjectID__Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

received := ""

dash := `{
"metadata":{
"name":"hello",
"id":"bb2ba294-d4b3-48bc-90a7-12dd56e9424b",
}
}`

httpmock.RegisterResponder("GET", "https://org.semaphoretext.xyz/api/v1alpha/projects/hello",
func(req *http.Request) (*http.Response, error) {
fmt.Println("GET /api/v1alpha/projects/hello")
return httpmock.NewStringResponse(200, dash), nil
},
)

httpmock.RegisterResponder("POST", "https://org.semaphoretext.xyz/api/v1/projects/hello/secrets",
func(req *http.Request) (*http.Response, error) {
body, _ := ioutil.ReadAll(req.Body)

received = string(body)

return httpmock.NewStringResponse(200, received), nil
},
)

content1 := "This is some docker config"
content2 := "This is some gcloud config"

ioutil.WriteFile("/tmp/docker", []byte(content1), 0644)
ioutil.WriteFile("/tmp/gcloud", []byte(content2), 0644)

// flags for env vars and projects stay the same as in the previous test

RootCmd.SetArgs([]string{
"create",
"secret",
"-i", "hello",
"projectABC",
})


RootCmd.Execute()


file1 := base64.StdEncoding.EncodeToString([]byte(content1))
file2 := base64.StdEncoding.EncodeToString([]byte(content2)) // We do not expect project_id_or_name to be received in the body of the request, as it's set by the grpc-gateway from the URL
expected := fmt.Sprintf(`{"apiVersion":"v1","kind":"ProjectSecret","metadata":{"name":"projectABC"},"data":{"env_vars":[{"name":"FOO","value":"BAR"},{"name":"ZEZ","value":"Hello World"}],"files":[{"path":".config/docker","content":"%s"},{"path":".config/gcloud","content":"%s"}]}}`, file1, file2)

assert.Equal(t, expected, received)
}

0 comments on commit 587ba17

Please sign in to comment.