1 package channels
2
3 import (
4 "context"
5 "database/sql"
6 "fmt"
7
8 iamAPI "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/iam/v1beta1"
9 secretmanagerAPI "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/secretmanager/v1beta1"
10 "sigs.k8s.io/controller-runtime/pkg/client"
11
12 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/k8s/v1alpha1"
13 "github.com/google/uuid"
14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15
16 "edge-infra.dev/pkg/edge/api/graph/model"
17 chariotClientApi "edge-infra.dev/pkg/edge/chariot/client"
18 "edge-infra.dev/pkg/edge/edgeencrypt"
19 "edge-infra.dev/pkg/lib/gcp/iam"
20 "edge-infra.dev/pkg/lib/gcp/iam/roles"
21 )
22
23 const sqlCreateChannelServiceAccount = `INSERT INTO channels_service_accounts(channel_id, serviceAccountEmail)
24 VALUES ($1, $2)
25 RETURNING channel_sa_id, channel_id, serviceAccountEmail, created_at`
26
27 func (cs *ChannelService) CreateChannelIAM(ctx context.Context, channelID uuid.UUID, saEmail string) (*model.ChannelIAMPolicy, error) {
28 tx, txErr := cs.db.BeginTx(ctx, &sql.TxOptions{})
29
30 if txErr != nil {
31 return nil, txErr
32 }
33
34 var row = cs.db.QueryRowContext(ctx, sqlCreateChannelServiceAccount,
35 channelID,
36 saEmail,
37 )
38
39 channelIAMPolicy, err := scanChannelServiceAccount(row)
40
41 if err != nil {
42 return nil, fmt.Errorf("failed to insert channel service account entry in sql: %w", err)
43 }
44
45 sendToChariotErr := cs.createChannelIAMPolicyBinding(ctx, channelIAMPolicy.ChannelSaID, channelIAMPolicy.SaEmail, cs.foremanProjectID)
46
47 if sendToChariotErr != nil {
48 if rollbackErr := tx.Rollback(); rollbackErr != nil {
49 return nil, rollbackErr
50 }
51 }
52
53 if txCommitErr := tx.Commit(); txCommitErr != nil {
54 return nil, txCommitErr
55 }
56
57 return &channelIAMPolicy, nil
58 }
59
60 func (cs *ChannelService) createChannelIAMPolicyBinding(ctx context.Context, channelSAID, saEmail, foremanProjectID string) error {
61 memberEmail := iam.SvcAccountMember(saEmail)
62
63 channelIAMPolicy := &iamAPI.IAMPolicyMember{
64 ObjectMeta: metav1.ObjectMeta{
65 Name: fmt.Sprintf("channel-iam-%s", channelSAID),
66 Namespace: edgeencrypt.DecryptionName,
67 },
68 TypeMeta: metav1.TypeMeta{
69 APIVersion: iamAPI.IAMPolicyMemberGVK.GroupVersion().String(),
70 Kind: iamAPI.IAMPolicyMemberGVK.Kind,
71 },
72 Spec: iamAPI.IAMPolicyMemberSpec{
73 Member: &memberEmail,
74 ResourceRef: v1alpha1.IAMResourceRef{
75 APIVersion: secretmanagerAPI.SecretManagerSecretGVK.GroupVersion().String(),
76 Kind: secretmanagerAPI.SecretManagerSecretGVK.Kind,
77 External: fmt.Sprintf("projects/%s/secrets/%s", foremanProjectID, edgeencrypt.DecryptionTokenSecretManager),
78 },
79 Role: roles.SecretAccessor,
80 },
81 }
82
83 sendToChariotErr := cs.sendChariotMessage(ctx, channelIAMPolicy)
84
85 if sendToChariotErr != nil {
86 return sendToChariotErr
87 }
88
89 return nil
90 }
91
92 func (cs *ChannelService) sendChariotMessage(ctx context.Context, object ...client.Object) error {
93 chariotMessage, err := chariotClientApi.
94 NewChariotMessage().
95 SetOperation(chariotClientApi.Create).
96 SetBanner(cs.foremanProjectID).
97 SetCluster("foreman0").
98 AddK8sBase64Object(object...)
99
100 if err != nil {
101 return err
102 }
103
104 if err := cs.ChariotService.InvokeChariotPubsub(ctx, chariotMessage, nil); err != nil {
105 return fmt.Errorf("error calling chariot v2: %w", err)
106 }
107
108 return nil
109 }
110
View as plain text