55 "fmt"
66 "regexp"
77 "strings"
8+ "sync"
89
910 "secrets-init/pkg/secrets" //nolint:gci
1011
@@ -15,6 +16,13 @@ import (
1516 secretspb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1" //nolint:gci
1617)
1718
19+ var fullSecretRe = regexp .MustCompile (`projects/[^/]+/secrets/[^/+](/version/[^/+])?` )
20+
21+ type result struct {
22+ Env string
23+ Err error
24+ }
25+
1826// SecretsProvider Google Cloud secrets provider
1927type SecretsProvider struct {
2028 sm SecretsManagerAPI
@@ -53,40 +61,79 @@ func NewGoogleSecretsProvider(ctx context.Context, projectID string) (secrets.Pr
5361func (sp SecretsProvider ) ResolveSecrets (ctx context.Context , vars []string ) ([]string , error ) {
5462 envs := make ([]string , 0 , len (vars ))
5563
56- fullSecretRe := regexp .MustCompile ("projects/[^/]+/secrets/[^/+](/version/[^/+])?" )
64+ // Create a channel to collect the results
65+ results := make (chan result , len (vars ))
5766
67+ // Start a goroutine for each secret
68+ var wg sync.WaitGroup
5869 for _ , env := range vars {
59- kv := strings .Split (env , "=" )
60- key , value := kv [0 ], kv [1 ]
61- if strings .HasPrefix (value , "gcp:secretmanager:" ) {
62- // construct valid secret name
63- name := strings .TrimPrefix (value , "gcp:secretmanager:" )
64-
65- isLong := fullSecretRe .MatchString (name )
66-
67- if ! isLong {
68- if sp .projectID == "" {
69- return vars , errors .Errorf ("failed to get secret \" %s\" from Google Secret Manager (unknown project)" , name )
70+ wg .Add (1 )
71+ go func (env string ) {
72+ defer wg .Done ()
73+ select {
74+ case <- ctx .Done ():
75+ results <- result {Err : ctx .Err ()}
76+ return
77+ default :
78+ val , err := sp .processEnvironmentVariable (ctx , env )
79+ if err != nil {
80+ results <- result {Err : err }
81+ return
7082 }
71- name = fmt . Sprintf ( "projects/%s/secrets/%s" , sp . projectID , name )
83+ results <- result { Env : val }
7284 }
85+ }(env )
86+ }
7387
74- // if no version specified add latest
75- if ! strings .Contains (name , "/versions/" ) {
76- name += "/versions/latest"
77- }
78- // get secret value
79- req := & secretspb.AccessSecretVersionRequest {
80- Name : name ,
81- }
82- secret , err := sp .sm .AccessSecretVersion (ctx , req )
83- if err != nil {
84- return vars , errors .Wrap (err , "failed to get secret from Google Secret Manager" )
85- }
86- env = key + "=" + string (secret .Payload .GetData ())
88+ // Start another goroutine to close the results channel when all fetch goroutines are done
89+ go func () {
90+ wg .Wait ()
91+ close (results )
92+ }()
93+
94+ // Collect the results
95+ for res := range results {
96+ if res .Err != nil {
97+ return vars , res .Err
8798 }
88- envs = append (envs , env )
99+ envs = append (envs , res . Env )
89100 }
90101
91102 return envs , nil
92103}
104+
105+ // processEnvironmentVariable processes the environment variable and replaces the value with the secret value
106+ func (sp SecretsProvider ) processEnvironmentVariable (ctx context.Context , env string ) (string , error ) {
107+ kv := strings .Split (env , "=" )
108+ key , value := kv [0 ], kv [1 ]
109+ if ! strings .HasPrefix (value , "gcp:secretmanager:" ) {
110+ return env , nil
111+ }
112+
113+ // construct valid secret name
114+ name := strings .TrimPrefix (value , "gcp:secretmanager:" )
115+
116+ isLong := fullSecretRe .MatchString (name )
117+
118+ if ! isLong {
119+ if sp .projectID == "" {
120+ return "" , errors .Errorf ("failed to get secret \" %s\" from Google Secret Manager (unknown project)" , name )
121+ }
122+ name = fmt .Sprintf ("projects/%s/secrets/%s" , sp .projectID , name )
123+ }
124+
125+ // if no version specified add latest
126+ if ! strings .Contains (name , "/versions/" ) {
127+ name += "/versions/latest"
128+ }
129+
130+ // get secret value
131+ req := & secretspb.AccessSecretVersionRequest {
132+ Name : name ,
133+ }
134+ secret , err := sp .sm .AccessSecretVersion (ctx , req )
135+ if err != nil {
136+ return "" , fmt .Errorf ("failed to get secret from Google Secret Manager: %w" , err )
137+ }
138+ return key + "=" + string (secret .Payload .GetData ()), nil
139+ }
0 commit comments