mirror of
https://github.com/actions/actions-runner-controller.git
synced 2025-12-12 20:46:47 +00:00
Azure Key Vault integration to resolve secrets (#4090)
This commit is contained in:
89
apis/actions.github.com/v1alpha1/appconfig/appconfig.go
Normal file
89
apis/actions.github.com/v1alpha1/appconfig/appconfig.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package appconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
type AppConfig struct {
|
||||
AppID string `json:"github_app_id"`
|
||||
AppInstallationID int64 `json:"github_app_installation_id"`
|
||||
AppPrivateKey string `json:"github_app_private_key"`
|
||||
|
||||
Token string `json:"github_token"`
|
||||
}
|
||||
|
||||
func (c *AppConfig) tidy() *AppConfig {
|
||||
if len(c.Token) > 0 {
|
||||
return &AppConfig{
|
||||
Token: c.Token,
|
||||
}
|
||||
}
|
||||
|
||||
return &AppConfig{
|
||||
AppID: c.AppID,
|
||||
AppInstallationID: c.AppInstallationID,
|
||||
AppPrivateKey: c.AppPrivateKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AppConfig) Validate() error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("missing app config")
|
||||
}
|
||||
hasToken := len(c.Token) > 0
|
||||
hasGitHubAppAuth := c.hasGitHubAppAuth()
|
||||
if hasToken && hasGitHubAppAuth {
|
||||
return fmt.Errorf("both PAT and GitHub App credentials provided. should only provide one")
|
||||
}
|
||||
if !hasToken && !hasGitHubAppAuth {
|
||||
return fmt.Errorf("no credentials provided: either a PAT or GitHub App credentials should be provided")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AppConfig) hasGitHubAppAuth() bool {
|
||||
return len(c.AppID) > 0 && c.AppInstallationID > 0 && len(c.AppPrivateKey) > 0
|
||||
}
|
||||
|
||||
func FromSecret(secret *corev1.Secret) (*AppConfig, error) {
|
||||
var appInstallationID int64
|
||||
if v := string(secret.Data["github_app_installation_id"]); v != "" {
|
||||
val, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appInstallationID = val
|
||||
}
|
||||
|
||||
cfg := &AppConfig{
|
||||
Token: string(secret.Data["github_token"]),
|
||||
AppID: string(secret.Data["github_app_id"]),
|
||||
AppInstallationID: appInstallationID,
|
||||
AppPrivateKey: string(secret.Data["github_app_private_key"]),
|
||||
}
|
||||
|
||||
if err := cfg.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate config: %v", err)
|
||||
}
|
||||
|
||||
return cfg.tidy(), nil
|
||||
}
|
||||
|
||||
func FromJSONString(v string) (*AppConfig, error) {
|
||||
var appConfig AppConfig
|
||||
if err := json.NewDecoder(bytes.NewBufferString(v)).Decode(&appConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := appConfig.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate app config decoded from string: %w", err)
|
||||
}
|
||||
|
||||
return appConfig.tidy(), nil
|
||||
}
|
||||
152
apis/actions.github.com/v1alpha1/appconfig/appconfig_test.go
Normal file
152
apis/actions.github.com/v1alpha1/appconfig/appconfig_test.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package appconfig
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestAppConfigValidate_invalid(t *testing.T) {
|
||||
tt := map[string]*AppConfig{
|
||||
"empty": {},
|
||||
"token and app config": {
|
||||
AppID: "1",
|
||||
AppInstallationID: 2,
|
||||
AppPrivateKey: "private key",
|
||||
Token: "token",
|
||||
},
|
||||
"app id not set": {
|
||||
AppInstallationID: 2,
|
||||
AppPrivateKey: "private key",
|
||||
},
|
||||
"app installation id not set": {
|
||||
AppID: "2",
|
||||
AppPrivateKey: "private key",
|
||||
},
|
||||
"private key empty": {
|
||||
AppID: "2",
|
||||
AppInstallationID: 1,
|
||||
AppPrivateKey: "",
|
||||
},
|
||||
}
|
||||
|
||||
for name, cfg := range tt {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
err := cfg.Validate()
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppConfigValidate_valid(t *testing.T) {
|
||||
tt := map[string]*AppConfig{
|
||||
"token": {
|
||||
Token: "token",
|
||||
},
|
||||
"app ID": {
|
||||
AppID: "1",
|
||||
AppInstallationID: 2,
|
||||
AppPrivateKey: "private key",
|
||||
},
|
||||
}
|
||||
|
||||
for name, cfg := range tt {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
err := cfg.Validate()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppConfigFromSecret_invalid(t *testing.T) {
|
||||
tt := map[string]map[string]string{
|
||||
"empty": {},
|
||||
"token and app provided": {
|
||||
"github_token": "token",
|
||||
"github_app_id": "2",
|
||||
"githu_app_installation_id": "3",
|
||||
"github_app_private_key": "private key",
|
||||
},
|
||||
"invalid app id": {
|
||||
"github_app_id": "abc",
|
||||
"githu_app_installation_id": "3",
|
||||
"github_app_private_key": "private key",
|
||||
},
|
||||
"invalid app installation_id": {
|
||||
"github_app_id": "1",
|
||||
"githu_app_installation_id": "abc",
|
||||
"github_app_private_key": "private key",
|
||||
},
|
||||
"empty private key": {
|
||||
"github_app_id": "1",
|
||||
"githu_app_installation_id": "2",
|
||||
"github_app_private_key": "",
|
||||
},
|
||||
}
|
||||
|
||||
for name, data := range tt {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
secret := &corev1.Secret{
|
||||
StringData: data,
|
||||
}
|
||||
|
||||
appConfig, err := FromSecret(secret)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, appConfig)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppConfigFromSecret_valid(t *testing.T) {
|
||||
tt := map[string]map[string]string{
|
||||
"with token": {
|
||||
"github_token": "token",
|
||||
},
|
||||
"app config": {
|
||||
"github_app_id": "2",
|
||||
"githu_app_installation_id": "3",
|
||||
"github_app_private_key": "private key",
|
||||
},
|
||||
}
|
||||
|
||||
for name, data := range tt {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
secret := &corev1.Secret{
|
||||
StringData: data,
|
||||
}
|
||||
|
||||
appConfig, err := FromSecret(secret)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, appConfig)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppConfigFromString_valid(t *testing.T) {
|
||||
tt := map[string]*AppConfig{
|
||||
"token": {
|
||||
Token: "token",
|
||||
},
|
||||
"app ID": {
|
||||
AppID: "1",
|
||||
AppInstallationID: 2,
|
||||
AppPrivateKey: "private key",
|
||||
},
|
||||
}
|
||||
|
||||
for name, cfg := range tt {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
bytes, err := json.Marshal(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := FromJSONString(string(bytes))
|
||||
require.NoError(t, err)
|
||||
|
||||
want := cfg.tidy()
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user