pkg/cmd/serviceaccount/delete.go (112 lines of code) (raw):
package serviceaccount
import (
"context"
"fmt"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/spf13/cobra"
"monis.app/mlog"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/Azure/azure-workload-identity/pkg/cloud"
"github.com/Azure/azure-workload-identity/pkg/cmd/serviceaccount/auth"
"github.com/Azure/azure-workload-identity/pkg/cmd/serviceaccount/options"
phases "github.com/Azure/azure-workload-identity/pkg/cmd/serviceaccount/phases/delete"
"github.com/Azure/azure-workload-identity/pkg/cmd/serviceaccount/phases/workflow"
"github.com/Azure/azure-workload-identity/pkg/cmd/serviceaccount/util"
"github.com/Azure/azure-workload-identity/pkg/kuberneteshelper"
)
var (
roleAssignmentPhase = phases.NewRoleAssignmentPhase()
federatedIdentityPhase = phases.NewFederatedIdentityPhase()
serviceAccountPhase = phases.NewServiceAccountPhase()
aadApplicationPhase = phases.NewAADApplicationPhase()
)
func newDeleteCmd(authProvider auth.Provider) *cobra.Command {
deleteRunner := workflow.NewPhaseRunner()
data := &deleteData{
authProvider: authProvider,
}
cmd := &cobra.Command{
Use: "delete",
RunE: func(cmd *cobra.Command, args []string) error {
// if we are running the AAD application delete phase, we can
// slightly optimize the delete command by skipping the federated-identity
// phase because it will get removed when the AAD application is removed.
if deleteRunner.IsPhaseActive(aadApplicationPhase) {
deleteRunner.AppendSkipPhases(federatedIdentityPhase)
}
return deleteRunner.Run(data)
},
}
f := cmd.Flags()
f.StringVar(&data.serviceAccountName, options.ServiceAccountName.Flag, "", options.ServiceAccountName.Description)
f.StringVar(&data.serviceAccountNamespace, options.ServiceAccountNamespace.Flag, "default", options.ServiceAccountNamespace.Description)
f.StringVar(&data.serviceAccountIssuerURL, options.ServiceAccountIssuerURL.Flag, "", options.ServiceAccountIssuerURL.Description)
f.StringVar(&data.aadApplicationName, options.AADApplicationName.Flag, "", options.AADApplicationName.Description)
f.StringVar(&data.aadApplicationObjectID, options.AADApplicationObjectID.Flag, "", options.AADApplicationObjectID.Description)
f.StringVar(&data.roleAssignmentID, options.RoleAssignmentID.Flag, "", options.RoleAssignmentID.Description)
// append phases in order
deleteRunner.AppendPhases(
roleAssignmentPhase,
federatedIdentityPhase,
serviceAccountPhase,
aadApplicationPhase,
)
deleteRunner.BindToCommand(cmd, data)
return cmd
}
// deleteData is an implementation of phases.DeleteData in
// pkg/cmd/serviceaccount/phases/delete/data.go
type deleteData struct {
serviceAccountName string
serviceAccountNamespace string
serviceAccountIssuerURL string
aadApplication models.Applicationable // cache
aadApplicationName string
aadApplicationObjectID string
roleAssignmentID string
authProvider auth.Provider
}
var _ phases.DeleteData = &deleteData{}
// ServiceAccountName returns the name of the service account.
func (d *deleteData) ServiceAccountName() string {
return d.serviceAccountName
}
// ServiceAccountNamespace returns the namespace of the service account.
func (d *deleteData) ServiceAccountNamespace() string {
return d.serviceAccountNamespace
}
// ServiceAccountIssuerURL returns the issuer URL of the service account.
func (d *deleteData) ServiceAccountIssuerURL() string {
return d.serviceAccountIssuerURL
}
// AADApplication returns the AAD application object.
// This will return the cached value if it has been created.
func (d *deleteData) AADApplication() (models.Applicationable, error) {
if d.aadApplication == nil {
app, err := d.AzureClient().GetApplication(context.Background(), d.AADApplicationName())
if err != nil {
return nil, err
}
d.aadApplication = app
}
return d.aadApplication, nil
}
// AADApplicationName returns the name of the AAD application.
func (d *deleteData) AADApplicationName() string {
name := d.aadApplicationName
if name == "" {
if d.ServiceAccountNamespace() != "" && d.ServiceAccountName() != "" && d.ServiceAccountIssuerURL() != "" {
mlog.Warning("--aad-application-name not specified, constructing name with service account namespace, name, and the hash of the issuer URL")
name = fmt.Sprintf("%s-%s-%s", d.ServiceAccountNamespace(), d.serviceAccountName, util.GetIssuerHash(d.ServiceAccountIssuerURL()))
}
}
return name
}
// AADApplicationObjectID returns the object ID of the AAD application.
// This will be used for creating or removing the federated identity credential.
func (d *deleteData) AADApplicationObjectID() string {
if d.aadApplicationObjectID != "" {
return d.aadApplicationObjectID
}
app, err := d.AADApplication()
if err != nil {
mlog.Error("failed to get AAD application object ID. Returning an empty string", err)
return ""
}
return *app.GetId()
}
// AzureClient returns the Azure client.
func (d *deleteData) RoleAssignmentID() string {
return d.roleAssignmentID
}
// AzureClient returns the Azure client.
func (d *deleteData) AzureClient() cloud.Interface {
return d.authProvider.GetAzureClient()
}
// KubeClient returns the Kubernetes client.
func (d *deleteData) KubeClient() (client.Client, error) {
return kuberneteshelper.GetKubeClient()
}