Skip to content

Commit f296c23

Browse files
github-actions[bot]bigkevmcdJonCrowthermatttrach
authored
feat: GitHub app provider (#1928)
Co-authored-by: Kevin McDermott <kevin.mcdermott@suse.com> Co-authored-by: Jonathan Crowther <jonathan.crowther@suse.com> Co-authored-by: Matt Trachier <matt.trachier@suse.com>
1 parent 0382d8a commit f296c23

9 files changed

Lines changed: 573 additions & 8 deletions

rancher2/00_provider_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ import (
99
)
1010

1111
func TestProvider(t *testing.T) {
12-
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
13-
assert.FailNow(t, "err: %s", err)
14-
}
12+
assert.NoError(t, Provider().(*schema.Provider).InternalValidate())
1513
}
1614

1715
func TestProvider_impl(t *testing.T) {

rancher2/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,8 @@ func getAuthConfigObject(kind string) (interface{}, error) {
12681268
return &managementClient.LdapConfig{}, nil
12691269
case managementClient.GithubConfigType:
12701270
return &managementClient.GithubConfig{}, nil
1271+
case managementClient.GithubAppConfigType:
1272+
return &managementClient.GithubAppConfig{}, nil
12711273
case managementClient.KeyCloakConfigType:
12721274
return &managementClient.KeyCloakConfig{}, nil
12731275
case managementClient.GenericOIDCConfigType:

rancher2/provider.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ func Provider() terraform.ResourceProvider {
113113
"rancher2_auth_config_azuread": resourceRancher2AuthConfigAzureAD(),
114114
"rancher2_auth_config_freeipa": resourceRancher2AuthConfigFreeIpa(),
115115
"rancher2_auth_config_github": resourceRancher2AuthConfigGithub(),
116+
"rancher2_auth_config_githubapp": resourceRancher2AuthConfigGithubApp(),
116117
"rancher2_auth_config_keycloak": resourceRancher2AuthConfigKeyCloak(),
117118
"rancher2_auth_config_okta": resourceRancher2AuthConfigOKTA(),
118119
"rancher2_auth_config_generic_oidc": resourceRancher2AuthConfigGenericOIDC(),
@@ -235,7 +236,7 @@ func providerValidateConfig(config *Config) (*Config, error) {
235236
if config.Bootstrap {
236237
// If bootstrap tokenkey accesskey nor secretkey can be provided
237238
if config.TokenKey != providerDefaultEmptyString {
238-
return &Config{}, fmt.Errorf("[ERROR] Bootsrap mode activated. Token_key or access_key and secret_key can not be provided")
239+
return &Config{}, fmt.Errorf("[ERROR] Bootsrap mode activated. token_key or access_key and secret_key can not be provided")
239240
}
240241
} else {
241242
// Else token or access key and secret key should be provided

rancher2/resource_rancher2_auth_config_github.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func resourceRancher2AuthConfigGithub() *schema.Resource {
2222
func resourceRancher2AuthConfigGithubCreate(d *schema.ResourceData, meta interface{}) error {
2323
client, err := meta.(*Config).ManagementClient()
2424
if err != nil {
25-
return err
25+
return fmt.Errorf("[ERROR] Failed to get ManagementClient %s", err)
2626
}
2727

2828
auth, err := client.AuthConfig.ByID(AuthConfigGithubName)
@@ -59,7 +59,7 @@ func resourceRancher2AuthConfigGithubRead(d *schema.ResourceData, meta interface
5959
log.Printf("[INFO] Refreshing Auth Config %s", AuthConfigGithubName)
6060
client, err := meta.(*Config).ManagementClient()
6161
if err != nil {
62-
return err
62+
return fmt.Errorf("[ERROR] Failed to get ManagementClient %s", err)
6363
}
6464

6565
auth, err := client.AuthConfig.ByID(AuthConfigGithubName)
@@ -96,7 +96,7 @@ func resourceRancher2AuthConfigGithubDelete(d *schema.ResourceData, meta interfa
9696

9797
client, err := meta.(*Config).ManagementClient()
9898
if err != nil {
99-
return err
99+
return fmt.Errorf("[ERROR] Failed to get ManagementClient %s", err)
100100
}
101101

102102
auth, err := client.AuthConfig.ByID(AuthConfigGithubName)
@@ -106,7 +106,7 @@ func resourceRancher2AuthConfigGithubDelete(d *schema.ResourceData, meta interfa
106106
d.SetId("")
107107
return nil
108108
}
109-
return err
109+
return fmt.Errorf("[ERROR] Getting the %s AuthConfig: %w", AuthConfigGithubName, err)
110110
}
111111

112112
if auth.Enabled == true {
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package rancher2
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
8+
managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
9+
)
10+
11+
func resourceRancher2AuthConfigGithubApp() *schema.Resource {
12+
return &schema.Resource{
13+
Create: resourceRancher2AuthConfigGithubAppCreate,
14+
Read: resourceRancher2AuthConfigGithubAppRead,
15+
Update: resourceRancher2AuthConfigGithubAppUpdate,
16+
Delete: resourceRancher2AuthConfigGithubAppDelete,
17+
Schema: authConfigGithubAppFields(),
18+
}
19+
}
20+
21+
func resourceRancher2AuthConfigGithubAppCreate(d *schema.ResourceData, meta interface{}) error {
22+
client, err := meta.(*Config).ManagementClient()
23+
if err != nil {
24+
return fmt.Errorf("[ERROR] Failed to get ManagementClient %s", err)
25+
}
26+
27+
auth, err := client.AuthConfig.ByID(AuthConfigGithubAppName)
28+
if err != nil {
29+
return fmt.Errorf("[ERROR] Failed to get Auth Config %s: %s", AuthConfigGithubAppName, err)
30+
}
31+
32+
log.Printf("[INFO] Creating Auth Config %s %s", AuthConfigGithubAppName, auth.Name)
33+
34+
authGithubApp, err := expandAuthConfigGithubApp(d)
35+
if err != nil {
36+
return fmt.Errorf("[ERROR] Failed expanding Auth Config %s: %s", AuthConfigGithubAppName, err)
37+
}
38+
39+
// Checking if other auth config is enabled
40+
if authGithubApp.Enabled {
41+
err = meta.(*Config).CheckAuthConfigEnabled(AuthConfigGithubAppName)
42+
if err != nil {
43+
return fmt.Errorf("[ERROR] Checking if an Auth Config other than %s is enabled: %s", AuthConfigGithubAppName, err)
44+
}
45+
}
46+
47+
// Updated auth config
48+
newAuth := &managementClient.GithubAppConfig{}
49+
err = meta.(*Config).UpdateAuthConfig(auth.Links["self"], authGithubApp, newAuth)
50+
if err != nil {
51+
return fmt.Errorf("[ERROR] Updating Auth Config %s: %s", AuthConfigGithubAppName, err)
52+
}
53+
54+
return resourceRancher2AuthConfigGithubAppRead(d, meta)
55+
}
56+
57+
func resourceRancher2AuthConfigGithubAppRead(d *schema.ResourceData, meta interface{}) error {
58+
log.Printf("[INFO] Refreshing Auth Config %s", AuthConfigGithubAppName)
59+
client, err := meta.(*Config).ManagementClient()
60+
if err != nil {
61+
return fmt.Errorf("[ERROR] Failed to get ManagementClient %s", err)
62+
}
63+
64+
auth, err := client.AuthConfig.ByID(AuthConfigGithubAppName)
65+
if err != nil {
66+
if IsNotFound(err) {
67+
log.Printf("[INFO] Auth Config %s not found.", AuthConfigGithubAppName)
68+
d.SetId("")
69+
return nil
70+
}
71+
return fmt.Errorf("[ERROR] Getting Auth Config %s By ID: %w", AuthConfigGithubAppName, err)
72+
}
73+
74+
authGithubApp, err := meta.(*Config).GetAuthConfig(auth)
75+
if err != nil {
76+
return fmt.Errorf("[ERROR] Getting Auth Config %s: %w", AuthConfigGithubAppName, err)
77+
}
78+
79+
err = flattenAuthConfigGithubApp(d, authGithubApp.(*managementClient.GithubAppConfig))
80+
if err != nil {
81+
return fmt.Errorf("[ERROR] Flattening GitHub app: %w", err)
82+
}
83+
84+
return nil
85+
}
86+
87+
func resourceRancher2AuthConfigGithubAppUpdate(d *schema.ResourceData, meta interface{}) error {
88+
log.Printf("[INFO] Updating Auth Config %s", AuthConfigGithubAppName)
89+
90+
return resourceRancher2AuthConfigGithubAppCreate(d, meta)
91+
}
92+
93+
func resourceRancher2AuthConfigGithubAppDelete(d *schema.ResourceData, meta interface{}) error {
94+
log.Printf("[INFO] Disabling Auth Config %s", AuthConfigGithubAppName)
95+
96+
client, err := meta.(*Config).ManagementClient()
97+
if err != nil {
98+
return fmt.Errorf("[ERROR] Failed to get ManagementClient %s", err)
99+
}
100+
101+
auth, err := client.AuthConfig.ByID(AuthConfigGithubAppName)
102+
if err != nil {
103+
if IsNotFound(err) {
104+
log.Printf("[INFO] Auth Config %s not found.", AuthConfigGithubAppName)
105+
d.SetId("")
106+
return nil
107+
}
108+
return fmt.Errorf("[ERROR] Getting the %s AuthConfig: %w", AuthConfigGithubAppName, err)
109+
}
110+
111+
if auth.Enabled == true {
112+
err = client.Post(auth.Actions["disable"], nil, nil)
113+
if err != nil {
114+
return fmt.Errorf("[ERROR] Disabling Auth Config %s: %s", AuthConfigGithubAppName, err)
115+
}
116+
}
117+
118+
d.SetId("")
119+
return nil
120+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package rancher2
2+
3+
import (
4+
"crypto/rsa"
5+
"crypto/x509"
6+
"encoding/pem"
7+
"fmt"
8+
"maps"
9+
"strconv"
10+
11+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
12+
)
13+
14+
const AuthConfigGithubAppName = "githubapp"
15+
16+
func authConfigGithubAppFields() map[string]*schema.Schema {
17+
s := map[string]*schema.Schema{
18+
"client_id": {
19+
Type: schema.TypeString,
20+
Required: true,
21+
Sensitive: true,
22+
},
23+
"client_secret": {
24+
Type: schema.TypeString,
25+
Required: true,
26+
Sensitive: true,
27+
},
28+
"hostname": {
29+
Type: schema.TypeString,
30+
Optional: true,
31+
Default: "github.com",
32+
},
33+
"tls": {
34+
Type: schema.TypeBool,
35+
Optional: true,
36+
Default: true,
37+
},
38+
"app_id": {
39+
Type: schema.TypeString,
40+
Description: "The GitHub App ID is provided on the GitHub apps page.",
41+
Required: true,
42+
ValidateFunc: isValidIntegerString,
43+
},
44+
"private_key": {
45+
Type: schema.TypeString,
46+
Required: true,
47+
Description: "PEM format private key for signing requests.",
48+
ValidateFunc: isPEMEncodedPrivateKey,
49+
},
50+
"installation_id": {
51+
Type: schema.TypeString,
52+
Description: "If the Installation ID is not provided, all installations for the App will be queried.",
53+
Optional: true,
54+
ValidateFunc: isValidIntegerString,
55+
},
56+
}
57+
58+
maps.Copy(s, authConfigFields())
59+
60+
return s
61+
}
62+
63+
func isValidIntegerString(i any, k string) (warnings []string, errors []error) {
64+
v, ok := i.(string)
65+
if !ok {
66+
errors = append(errors, fmt.Errorf("expected type of %q to be string", k))
67+
return warnings, errors
68+
}
69+
70+
if _, err := strconv.ParseInt(v, 10, 64); err != nil {
71+
errors = append(errors, fmt.Errorf("expected %q to be a valid integer, got %v", k, v))
72+
}
73+
74+
return warnings, errors
75+
}
76+
77+
func isPEMEncodedPrivateKey(i any, k string) (warnings []string, errors []error) {
78+
v, ok := i.(string)
79+
if !ok {
80+
errors = append(errors, fmt.Errorf("expected type of %q to be string", k))
81+
return warnings, errors
82+
}
83+
84+
var block *pem.Block
85+
if block, _ = pem.Decode([]byte(v)); block == nil {
86+
errors = append(errors, fmt.Errorf("expected %q to be PEM encoded", k))
87+
return warnings, errors
88+
}
89+
90+
var key any
91+
parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
92+
if err != nil {
93+
parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
94+
if err != nil {
95+
errors = append(errors, fmt.Errorf("expected %q to be an RSA Private Key", k))
96+
return warnings, errors
97+
} else {
98+
key = parsedKey
99+
}
100+
} else {
101+
key = parsedKey
102+
}
103+
104+
if _, ok := key.(*rsa.PrivateKey); !ok {
105+
errors = append(errors, fmt.Errorf("expected %q to be an RSA Private Key", k))
106+
return warnings, errors
107+
}
108+
109+
return warnings, errors
110+
}

0 commit comments

Comments
 (0)