I would like to retrieve a Github secret content from a script via Golang, that is executed from Github actions.
In this particular situation, the secret value stored in Github secrets has a space. I mean the secret value is: JWT <token-string>
.
The way to retrieve Github secrets from any script in any language as long they are executed in Github actions runners is by reading them as environment variables. So what I am doing is to read it in this way: (Please see the Authorization:
element in the slice of string below)
func MyTestFunction(t *testing.T, serverURL string) {
type AppLogin struct {
token string
}
method := "GET"
headers := map[string][]string{
"Content-Type": []string{"application/json, text/plain, */*"},
"Authorization": []string{os.Getenv("USER_JWT")},
}
The thing is that I am not getting the value from Github secrets when running Github action runner. I know this is happening since I tried to print it in this way, but nothing comes up:
fmt.Println("My JWT", os.Getenv("USER_JWT"))
I am afraid it is happening because that space between "JWT "
and the token, I mean JWT <token-string>
.
Here says:
Secret names can only contain alphanumeric characters ([a-z], [A-Z], [0-9]) or underscores (_). Spaces are not allowed.
As an important fact, my token secret value also contains .
character in its value. The value is something like this:
JWT xxxxxxx8888xxxxdsdsfsfsf9.eyJxxxxxxx8888xxxxdsdsfsfsf9.Tfgxadsdsfsfsasasad_s7sdsdsfgsgcs
So I believe, that is the reason why I cannot get the secret value.
I am not sure how I can fetch this from my Golang script, I even tried to modify the Github secret value just having it as a value the <token-string>
in order to avoid the space in the value, and I am calling it from go in this way:
"Authorization": []string{"JWT ", os.Getenv("SPECKLE_USER_JWT")}
But it did not work.
I read here that when calling secrets with special characters from github actions we have to escape them with single quotes ' '
but this process is from .yaml
file github actions.
The previous solution alternatives I am trying to, they works on my local machine, since my bash cli is able to get environment variables with spaces in their values. I am not sure how can I - let's say "escape" - a secret with space in a string as I have from golang.
I managed to read the JWT secret stored in GitHub secrets from a GitHub action executing the golang terratest code.
As mentioned, since Github secrets does not allow spaces " "
and dots .
characters and the token has some dots plus one space, that I did first was to encode it
echo -n '<token-value>' | base64
This generates a whole string without .
or spaces and then I stored this value on Github secrets. And I read it from golang in this way:
func main() {
var t *testing.T
serverURL := os.Getenv("SERVER_URL")
MyTestFunction(t, serverURL)
}
func MyTestFunction(t *testing.T, serverURL string) {
type SpeckleLogin struct {
token string
}
method := "GET"
// The encoded token is read from github secrets
b64EncodeJwt := os.Getenv("USER_JWT_ENCODE")
// fmt.Println("The encode JWT is:", b64EncodeJwt)
// The encoded read token is decoded
b64DecodeJwt, _ := b64.StdEncoding.DecodeString(b64EncodeJwt)
// fmt.Println("JWT Decoded", string(b64DecodeJwt))
// fmt.Println()
headers := map[string][]string{
"Content-Type": []string{"application/json, text/plain, */*"},
// The content of the token already decoded is included in the headers slice of strings.
"Authorization": []string{(string(b64DecodeJwt))},
}
jsonLogin := []byte(fmt.Sprintf(`{
"email":"%s",
"password": "%s"
}`, os.Getenv("USER_EMAIL"), os.Getenv("USER_PASSWORD")))
// The HTTP request is created
reqLogin, errReq := http.NewRequest(method, serverURL+"/api/accounts", bytes.NewBuffer(jsonLogin))
// The headers are added to the HTTP request
reqLogin.Header = headers
if errReq != nil {
messageReq := fmt.Sprintf("Error GET login request: %s", errReq.Error())
t.Fatal(messageReq)
}
clientLogin := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
// Sending the request
respLogin, errResp := clientLogin.Do(reqLogin)
if errResp != nil {
messageResp := fmt.Sprintf("Error GET login response: %s", errResp.Error())
t.Fatal(messageResp)
}
defer respLogin.Body.Close()
body, _ := ioutil.ReadAll(respLogin.Body)
// fmt.Println("BODY IS:")
// fmt.Println(string(body))
var speckleLogin map[string]interface{}
if err := json.Unmarshal([]byte(body), &speckleLogin); err != nil {
t.Fatal("Could not unmarshal json")
}
// We take the API token from the response
data := speckleLogin["resource"].(map[string]interface{})["apitoken"]
if speckleToken, ok := data.(string); ok {
// Here we assert the token is not empty
assert.NotEmpty(t, speckleToken)
}
But in addition as @WishwaPerera try to tell me, the new environment variable i am using from golang called SPECKLE_USER_JWT_ENCODE
above, has to be included in my github actions at the moment to run these tests from go test
command. So my github action .yaml
file finally is in this way:
name: Preview_Workflow
on:
pull_request:
branches:
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@master
- name: Install terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: 0.13.5
terraform_wrapper: false
- name: 'Terraform Version'
shell: bash
run: |
terraform version
- name: 'Login via Azure CLI'
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: 'Setup Go'
id: go
uses: actions/setup-go@v2
with:
go-version: '^1.16.5'
- name: 'Run Terratest'
id: terratest
run: |
cd tests
go get -u github.com/Azure/azure-storage-blob-go/azblob
go get -u github.com/gruntwork-io/terratest/modules/terraform
go get -u github.com/stretchr/testify/assert
// executing the test
go test
env:
SERVER_URL: "https://my-service-application-url"
USER_EMAIL: ${{ secrets.USER_EMAIL }}
USER_PASSWORD: ${{ secrets.USER_PASSWORD }}
USER_JWT_ENCODE: ${{ secrets.USER_JWT_ENCODE }}
# I am using these other ones to connect to azure.
ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
- name: Azure logout
run: |
az logout
A nice reference to understand a bit how to handle the HTTP package