package channels import ( "context" "database/sql" "fmt" iamAPI "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/iam/v1beta1" secretmanagerAPI "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/secretmanager/v1beta1" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/k8s/v1alpha1" "github.com/google/uuid" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "edge-infra.dev/pkg/edge/api/graph/model" chariotClientApi "edge-infra.dev/pkg/edge/chariot/client" "edge-infra.dev/pkg/edge/edgeencrypt" "edge-infra.dev/pkg/lib/gcp/iam" "edge-infra.dev/pkg/lib/gcp/iam/roles" ) const sqlCreateChannelServiceAccount = `INSERT INTO channels_service_accounts(channel_id, serviceAccountEmail) VALUES ($1, $2) RETURNING channel_sa_id, channel_id, serviceAccountEmail, created_at` func (cs *ChannelService) CreateChannelIAM(ctx context.Context, channelID uuid.UUID, saEmail string) (*model.ChannelIAMPolicy, error) { tx, txErr := cs.db.BeginTx(ctx, &sql.TxOptions{}) if txErr != nil { return nil, txErr } var row = cs.db.QueryRowContext(ctx, sqlCreateChannelServiceAccount, channelID, saEmail, ) channelIAMPolicy, err := scanChannelServiceAccount(row) if err != nil { return nil, fmt.Errorf("failed to insert channel service account entry in sql: %w", err) } sendToChariotErr := cs.createChannelIAMPolicyBinding(ctx, channelIAMPolicy.ChannelSaID, channelIAMPolicy.SaEmail, cs.foremanProjectID) if sendToChariotErr != nil { if rollbackErr := tx.Rollback(); rollbackErr != nil { return nil, rollbackErr } } if txCommitErr := tx.Commit(); txCommitErr != nil { return nil, txCommitErr } return &channelIAMPolicy, nil } func (cs *ChannelService) createChannelIAMPolicyBinding(ctx context.Context, channelSAID, saEmail, foremanProjectID string) error { memberEmail := iam.SvcAccountMember(saEmail) channelIAMPolicy := &iamAPI.IAMPolicyMember{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("channel-iam-%s", channelSAID), Namespace: edgeencrypt.DecryptionName, }, TypeMeta: metav1.TypeMeta{ APIVersion: iamAPI.IAMPolicyMemberGVK.GroupVersion().String(), Kind: iamAPI.IAMPolicyMemberGVK.Kind, }, Spec: iamAPI.IAMPolicyMemberSpec{ Member: &memberEmail, ResourceRef: v1alpha1.IAMResourceRef{ APIVersion: secretmanagerAPI.SecretManagerSecretGVK.GroupVersion().String(), Kind: secretmanagerAPI.SecretManagerSecretGVK.Kind, External: fmt.Sprintf("projects/%s/secrets/%s", foremanProjectID, edgeencrypt.DecryptionTokenSecretManager), }, Role: roles.SecretAccessor, }, } sendToChariotErr := cs.sendChariotMessage(ctx, channelIAMPolicy) if sendToChariotErr != nil { return sendToChariotErr } return nil } func (cs *ChannelService) sendChariotMessage(ctx context.Context, object ...client.Object) error { chariotMessage, err := chariotClientApi. NewChariotMessage(). SetOperation(chariotClientApi.Create). SetBanner(cs.foremanProjectID). SetCluster("foreman0"). AddK8sBase64Object(object...) if err != nil { return err } if err := cs.ChariotService.InvokeChariotPubsub(ctx, chariotMessage, nil); err != nil { return fmt.Errorf("error calling chariot v2: %w", err) } return nil }