func (rrn RelativeResourceNamer) ProjectName() string {
	return fmt.Sprintf("projects/%s", rrn.projectName)
}
ProjectName yields the relative resource name for a GCP project
package iam
import (
	"context"
	"fmt"
	"time"
	iamv1 "google.golang.org/api/iam/v1"
	"k8s.io/client-go/util/workqueue"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/crossplane/crossplane-runtime/pkg/errors"
	"github.com/crossplane/crossplane-runtime/pkg/event"
	"github.com/crossplane/crossplane-runtime/pkg/logging"
	"github.com/crossplane/crossplane-runtime/pkg/meta"
	"github.com/crossplane/crossplane-runtime/pkg/ratelimiter"
	"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
	"github.com/crossplane/crossplane-runtime/pkg/resource"
	"github.com/crossplane/provider-gcp/apis/iam/v1alpha1"
	gcp "github.com/crossplane/provider-gcp/pkg/clients"
	"github.com/crossplane/provider-gcp/pkg/clients/serviceaccount"
)
const (
	errNewClient         = "cannot create new GCP IAM API client"
	errNotServiceAccount = "managed resource is not a GCP ServiceAccount"
	errGet               = "cannot get GCP ServiceAccount object via IAM API"
	errCreate            = "cannot create GCP ServiceAccount object via IAM API"
	errUpdate            = "cannot update GCP ServiceAccount object via IAM API"
	errDelete            = "cannot delete GCP ServiceAccount object via IAM API"
)
func SetupServiceAccount(mgr ctrl.Manager, l logging.Logger, rl workqueue.RateLimiter, poll time.Duration) error {
	name := managed.ControllerName(v1alpha1.ServiceAccountGroupKind)
	return ctrl.NewControllerManagedBy(mgr).
		Named(name).
		WithOptions(controller.Options{
			RateLimiter: ratelimiter.NewDefaultManagedRateLimiter(rl),
		}).
		For(&v1alpha1.ServiceAccount{}).
		Complete(managed.NewReconciler(mgr,
			resource.ManagedKind(v1alpha1.ServiceAccountGroupVersionKind),
			managed.WithExternalConnecter(&connecter{client: mgr.GetClient()}),
			managed.WithPollInterval(poll),
			managed.WithLogger(l.WithValues("controller", name)),
			managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name)))))
}
type connecter struct {
	client client.Client
}
func (c *connecter) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) {
	projectID, opts, err := gcp.GetAuthInfo(ctx, c.client, mg)
	if err != nil {
		return nil, err
	}
	s, err := iamv1.NewService(ctx, opts)
	if err != nil {
		return nil, errors.Wrap(err, errNewClient)
	}
	rrn := NewRelativeResourceNamer(projectID)
	return &external{serviceAccounts: s.Projects.ServiceAccounts, rrn: rrn}, errors.Wrap(err, errNewClient)
}
type external struct {
	serviceAccounts serviceaccount.Client
	rrn             RelativeResourceNamer
}
func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) {
	cr, ok := mg.(*v1alpha1.ServiceAccount)
	if !ok {
		return managed.ExternalObservation{}, errors.New(errNotServiceAccount)
	}
	req := e.serviceAccounts.Get(e.rrn.ResourceName(cr))
	fromProvider, err := req.Context(ctx).Do()
	if gcp.IsErrorNotFound(err) {
		return managed.ExternalObservation{ResourceExists: false}, nil
	}
	if err != nil {
		return managed.ExternalObservation{}, errors.Wrap(err, errGet)
	}
	populateCRFromProvider(cr, fromProvider)
	if fromProvider.Email != "" {
		cr.Status.SetConditions(xpv1.Available())
	}
	return managed.ExternalObservation{
		ResourceExists:    true,
		ResourceUpToDate:  isUpToDate(&cr.Spec.ForProvider, fromProvider),
		ConnectionDetails: managed.ConnectionDetails{},
	}, nil
}
func (e *external) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) {
	cr, ok := mg.(*v1alpha1.ServiceAccount)
	if !ok {
		return managed.ExternalCreation{}, errors.New(errNotServiceAccount)
	}
	csar := &iamv1.CreateServiceAccountRequest{
		AccountId: meta.GetExternalName(cr),
		ServiceAccount: &iamv1.ServiceAccount{
			DisplayName: gcp.StringValue(cr.Spec.ForProvider.DisplayName),
			Description: gcp.StringValue(cr.Spec.ForProvider.Description),
		},
	}
	req := e.serviceAccounts.Create(e.rrn.ProjectName(), csar)
	fromProvider, err := req.Context(ctx).Do()
	if err != nil {
		return managed.ExternalCreation{}, errors.Wrap(err, errCreate)
	}
	populateCRFromProvider(cr, fromProvider)
	return managed.ExternalCreation{}, errors.Wrap(err, errCreate)
}
func (e *external) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) {
	cr, ok := mg.(*v1alpha1.ServiceAccount)
	if !ok {
		return managed.ExternalUpdate{}, errors.New(errNotServiceAccount)
	}
	sa := &iamv1.ServiceAccount{}
	populateProviderFromCR(sa, cr)
	psar := &iamv1.PatchServiceAccountRequest{
		ServiceAccount: sa,
		UpdateMask:     "description,displayName",
	}
	req := e.serviceAccounts.Patch(e.rrn.ResourceName(cr), psar)
	_, err := req.Context(ctx).Do()
	return managed.ExternalUpdate{}, errors.Wrap(err, errUpdate)
}
func (e *external) Delete(ctx context.Context, mg resource.Managed) error {
	cr, ok := mg.(*v1alpha1.ServiceAccount)
	if !ok {
		return errors.New(errNotServiceAccount)
	}
	req := e.serviceAccounts.Delete(e.rrn.ResourceName(cr))
	_, err := req.Context(ctx).Do()
	if gcp.IsErrorNotFound(err) {
		return nil
	}
	return errors.Wrap(err, errDelete)
}
func isUpToDate(in *v1alpha1.ServiceAccountParameters, observed *iamv1.ServiceAccount) bool {
	if in.DisplayName != nil && *in.DisplayName != observed.DisplayName {
		return false
	}
	if in.Description != nil && *in.Description != observed.Description {
		return false
	}
	return true
}
func populateCRFromProvider(cr *v1alpha1.ServiceAccount, fromProvider *iamv1.ServiceAccount) {
	cr.Status.AtProvider.UniqueID = fromProvider.UniqueId
	cr.Status.AtProvider.Email = fromProvider.Email
	cr.Status.AtProvider.Oauth2ClientID = fromProvider.Oauth2ClientId
	cr.Status.AtProvider.Disabled = fromProvider.Disabled
	cr.Status.AtProvider.Name = fromProvider.Name
}
func populateProviderFromCR(forProvider *iamv1.ServiceAccount, cr *v1alpha1.ServiceAccount) {
	forProvider.DisplayName = gcp.StringValue(cr.Spec.ForProvider.DisplayName)
	forProvider.Description = gcp.StringValue(cr.Spec.ForProvider.Description)
}
func NewRelativeResourceNamer(projectName string) RelativeResourceNamer {
	return RelativeResourceNamer{projectName: projectName}
}
type RelativeResourceNamer struct {
	projectName string
}