From 768c559fcc9006201c9481eb085141cfa4dc5477 Mon Sep 17 00:00:00 2001 From: Atze de Vries Date: Mon, 9 Dec 2024 14:15:53 +0100 Subject: [PATCH 1/4] feat: Add Repository Environment Resource closes: https://github.com/crossplane-contrib/provider-upjet-github/issues/87 --- .../v1alpha1/zz_environment_terraformed.go | 129 ++++ apis/repo/v1alpha1/zz_environment_types.go | 266 +++++++ .../v1alpha1/zz_generated.conversion_hubs.go | 3 + apis/repo/v1alpha1/zz_generated.deepcopy.go | 467 ++++++++++++ apis/repo/v1alpha1/zz_generated.managed.go | 60 ++ .../repo/v1alpha1/zz_generated.managedlist.go | 9 + apis/repo/v1alpha1/zz_generated.resolvers.go | 42 ++ config/external_name.go | 2 + config/repository/config.go | 9 + .../repo/v1alpha1/environment.yaml | 36 + .../repo/environment/zz_controller.go | 91 +++ internal/controller/zz_setup.go | 2 + .../repo.github.upbound.io_environments.yaml | 677 ++++++++++++++++++ 13 files changed, 1793 insertions(+) create mode 100755 apis/repo/v1alpha1/zz_environment_terraformed.go create mode 100755 apis/repo/v1alpha1/zz_environment_types.go create mode 100644 examples-generated/repo/v1alpha1/environment.yaml create mode 100755 internal/controller/repo/environment/zz_controller.go create mode 100644 package/crds/repo.github.upbound.io_environments.yaml diff --git a/apis/repo/v1alpha1/zz_environment_terraformed.go b/apis/repo/v1alpha1/zz_environment_terraformed.go new file mode 100755 index 0000000..0379f40 --- /dev/null +++ b/apis/repo/v1alpha1/zz_environment_terraformed.go @@ -0,0 +1,129 @@ +/* +Copyright 2022 Upbound Inc. +*/ + +// Code generated by upjet. DO NOT EDIT. + +package v1alpha1 + +import ( + "dario.cat/mergo" + "github.com/pkg/errors" + + "github.com/crossplane/upjet/pkg/resource" + "github.com/crossplane/upjet/pkg/resource/json" +) + +// GetTerraformResourceType returns Terraform resource type for this Environment +func (mg *Environment) GetTerraformResourceType() string { + return "github_repository_environment" +} + +// GetConnectionDetailsMapping for this Environment +func (tr *Environment) GetConnectionDetailsMapping() map[string]string { + return nil +} + +// GetObservation of this Environment +func (tr *Environment) GetObservation() (map[string]any, error) { + o, err := json.TFParser.Marshal(tr.Status.AtProvider) + if err != nil { + return nil, err + } + base := map[string]any{} + return base, json.TFParser.Unmarshal(o, &base) +} + +// SetObservation for this Environment +func (tr *Environment) SetObservation(obs map[string]any) error { + p, err := json.TFParser.Marshal(obs) + if err != nil { + return err + } + return json.TFParser.Unmarshal(p, &tr.Status.AtProvider) +} + +// GetID returns ID of underlying Terraform resource of this Environment +func (tr *Environment) GetID() string { + if tr.Status.AtProvider.ID == nil { + return "" + } + return *tr.Status.AtProvider.ID +} + +// GetParameters of this Environment +func (tr *Environment) GetParameters() (map[string]any, error) { + p, err := json.TFParser.Marshal(tr.Spec.ForProvider) + if err != nil { + return nil, err + } + base := map[string]any{} + return base, json.TFParser.Unmarshal(p, &base) +} + +// SetParameters for this Environment +func (tr *Environment) SetParameters(params map[string]any) error { + p, err := json.TFParser.Marshal(params) + if err != nil { + return err + } + return json.TFParser.Unmarshal(p, &tr.Spec.ForProvider) +} + +// GetInitParameters of this Environment +func (tr *Environment) GetInitParameters() (map[string]any, error) { + p, err := json.TFParser.Marshal(tr.Spec.InitProvider) + if err != nil { + return nil, err + } + base := map[string]any{} + return base, json.TFParser.Unmarshal(p, &base) +} + +// GetInitParameters of this Environment +func (tr *Environment) GetMergedParameters(shouldMergeInitProvider bool) (map[string]any, error) { + params, err := tr.GetParameters() + if err != nil { + return nil, errors.Wrapf(err, "cannot get parameters for resource '%q'", tr.GetName()) + } + if !shouldMergeInitProvider { + return params, nil + } + + initParams, err := tr.GetInitParameters() + if err != nil { + return nil, errors.Wrapf(err, "cannot get init parameters for resource '%q'", tr.GetName()) + } + + // Note(lsviben): mergo.WithSliceDeepCopy is needed to merge the + // slices from the initProvider to forProvider. As it also sets + // overwrite to true, we need to set it back to false, we don't + // want to overwrite the forProvider fields with the initProvider + // fields. + err = mergo.Merge(¶ms, initParams, mergo.WithSliceDeepCopy, func(c *mergo.Config) { + c.Overwrite = false + }) + if err != nil { + return nil, errors.Wrapf(err, "cannot merge spec.initProvider and spec.forProvider parameters for resource '%q'", tr.GetName()) + } + + return params, nil +} + +// LateInitialize this Environment using its observed tfState. +// returns True if there are any spec changes for the resource. +func (tr *Environment) LateInitialize(attrs []byte) (bool, error) { + params := &EnvironmentParameters{} + if err := json.TFParser.Unmarshal(attrs, params); err != nil { + return false, errors.Wrap(err, "failed to unmarshal Terraform state parameters for late-initialization") + } + opts := []resource.GenericLateInitializerOption{resource.WithZeroValueJSONOmitEmptyFilter(resource.CNameWildcard)} + + li := resource.NewGenericLateInitializer(opts...) + return li.LateInitialize(&tr.Spec.ForProvider, params) +} + +// GetTerraformSchemaVersion returns the associated Terraform schema version +func (tr *Environment) GetTerraformSchemaVersion() int { + return 0 +} diff --git a/apis/repo/v1alpha1/zz_environment_types.go b/apis/repo/v1alpha1/zz_environment_types.go new file mode 100755 index 0000000..ccf0c68 --- /dev/null +++ b/apis/repo/v1alpha1/zz_environment_types.go @@ -0,0 +1,266 @@ +/* +Copyright 2022 Upbound Inc. +*/ + +// Code generated by upjet. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + v1 "github.com/crossplane/crossplane-runtime/apis/common/v1" +) + +type DeploymentBranchPolicyInitParameters struct { + + // Whether only branches that match the specified name patterns can deploy to this environment. + // Whether only branches that match the specified name patterns can deploy to this environment. + CustomBranchPolicies *bool `json:"customBranchPolicies,omitempty" tf:"custom_branch_policies,omitempty"` + + // Whether only branches with branch protection rules can deploy to this environment. + // Whether only branches with branch protection rules can deploy to this environment. + ProtectedBranches *bool `json:"protectedBranches,omitempty" tf:"protected_branches,omitempty"` +} + +type DeploymentBranchPolicyObservation struct { + + // Whether only branches that match the specified name patterns can deploy to this environment. + // Whether only branches that match the specified name patterns can deploy to this environment. + CustomBranchPolicies *bool `json:"customBranchPolicies,omitempty" tf:"custom_branch_policies,omitempty"` + + // Whether only branches with branch protection rules can deploy to this environment. + // Whether only branches with branch protection rules can deploy to this environment. + ProtectedBranches *bool `json:"protectedBranches,omitempty" tf:"protected_branches,omitempty"` +} + +type DeploymentBranchPolicyParameters struct { + + // Whether only branches that match the specified name patterns can deploy to this environment. + // Whether only branches that match the specified name patterns can deploy to this environment. + // +kubebuilder:validation:Optional + CustomBranchPolicies *bool `json:"customBranchPolicies" tf:"custom_branch_policies,omitempty"` + + // Whether only branches with branch protection rules can deploy to this environment. + // Whether only branches with branch protection rules can deploy to this environment. + // +kubebuilder:validation:Optional + ProtectedBranches *bool `json:"protectedBranches" tf:"protected_branches,omitempty"` +} + +type EnvironmentInitParameters struct { + + // Can repository admins bypass the environment protections. Defaults to true. + // Can Admins bypass deployment protections + CanAdminsBypass *bool `json:"canAdminsBypass,omitempty" tf:"can_admins_bypass,omitempty"` + + // The deployment branch policy configuration + DeploymentBranchPolicy []DeploymentBranchPolicyInitParameters `json:"deploymentBranchPolicy,omitempty" tf:"deployment_branch_policy,omitempty"` + + // The name of the environment. + // The name of the environment. + Environment *string `json:"environment,omitempty" tf:"environment,omitempty"` + + // Whether or not a user who created the job is prevented from approving their own job. Defaults to false. + // Prevent users from approving workflows runs that they triggered. + PreventSelfReview *bool `json:"preventSelfReview,omitempty" tf:"prevent_self_review,omitempty"` + + // The repository of the environment. + // The repository of the environment. + // +crossplane:generate:reference:type=Repository + Repository *string `json:"repository,omitempty" tf:"repository,omitempty"` + + // Reference to a Repository to populate repository. + // +kubebuilder:validation:Optional + RepositoryRef *v1.Reference `json:"repositoryRef,omitempty" tf:"-"` + + // Selector for a Repository to populate repository. + // +kubebuilder:validation:Optional + RepositorySelector *v1.Selector `json:"repositorySelector,omitempty" tf:"-"` + + // The environment reviewers configuration. + Reviewers []ReviewersInitParameters `json:"reviewers,omitempty" tf:"reviewers,omitempty"` + + // Amount of time to delay a job after the job is initially triggered. + // Amount of time to delay a job after the job is initially triggered. + WaitTimer *int64 `json:"waitTimer,omitempty" tf:"wait_timer,omitempty"` +} + +type EnvironmentObservation struct { + + // Can repository admins bypass the environment protections. Defaults to true. + // Can Admins bypass deployment protections + CanAdminsBypass *bool `json:"canAdminsBypass,omitempty" tf:"can_admins_bypass,omitempty"` + + // The deployment branch policy configuration + DeploymentBranchPolicy []DeploymentBranchPolicyObservation `json:"deploymentBranchPolicy,omitempty" tf:"deployment_branch_policy,omitempty"` + + // The name of the environment. + // The name of the environment. + Environment *string `json:"environment,omitempty" tf:"environment,omitempty"` + + ID *string `json:"id,omitempty" tf:"id,omitempty"` + + // Whether or not a user who created the job is prevented from approving their own job. Defaults to false. + // Prevent users from approving workflows runs that they triggered. + PreventSelfReview *bool `json:"preventSelfReview,omitempty" tf:"prevent_self_review,omitempty"` + + // The repository of the environment. + // The repository of the environment. + Repository *string `json:"repository,omitempty" tf:"repository,omitempty"` + + // The environment reviewers configuration. + Reviewers []ReviewersObservation `json:"reviewers,omitempty" tf:"reviewers,omitempty"` + + // Amount of time to delay a job after the job is initially triggered. + // Amount of time to delay a job after the job is initially triggered. + WaitTimer *int64 `json:"waitTimer,omitempty" tf:"wait_timer,omitempty"` +} + +type EnvironmentParameters struct { + + // Can repository admins bypass the environment protections. Defaults to true. + // Can Admins bypass deployment protections + // +kubebuilder:validation:Optional + CanAdminsBypass *bool `json:"canAdminsBypass,omitempty" tf:"can_admins_bypass,omitempty"` + + // The deployment branch policy configuration + // +kubebuilder:validation:Optional + DeploymentBranchPolicy []DeploymentBranchPolicyParameters `json:"deploymentBranchPolicy,omitempty" tf:"deployment_branch_policy,omitempty"` + + // The name of the environment. + // The name of the environment. + // +kubebuilder:validation:Optional + Environment *string `json:"environment,omitempty" tf:"environment,omitempty"` + + // Whether or not a user who created the job is prevented from approving their own job. Defaults to false. + // Prevent users from approving workflows runs that they triggered. + // +kubebuilder:validation:Optional + PreventSelfReview *bool `json:"preventSelfReview,omitempty" tf:"prevent_self_review,omitempty"` + + // The repository of the environment. + // The repository of the environment. + // +crossplane:generate:reference:type=Repository + // +kubebuilder:validation:Optional + Repository *string `json:"repository,omitempty" tf:"repository,omitempty"` + + // Reference to a Repository to populate repository. + // +kubebuilder:validation:Optional + RepositoryRef *v1.Reference `json:"repositoryRef,omitempty" tf:"-"` + + // Selector for a Repository to populate repository. + // +kubebuilder:validation:Optional + RepositorySelector *v1.Selector `json:"repositorySelector,omitempty" tf:"-"` + + // The environment reviewers configuration. + // +kubebuilder:validation:Optional + Reviewers []ReviewersParameters `json:"reviewers,omitempty" tf:"reviewers,omitempty"` + + // Amount of time to delay a job after the job is initially triggered. + // Amount of time to delay a job after the job is initially triggered. + // +kubebuilder:validation:Optional + WaitTimer *int64 `json:"waitTimer,omitempty" tf:"wait_timer,omitempty"` +} + +type ReviewersInitParameters struct { + + // Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // +listType=set + Teams []*int64 `json:"teams,omitempty" tf:"teams,omitempty"` + + // Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // +listType=set + Users []*int64 `json:"users,omitempty" tf:"users,omitempty"` +} + +type ReviewersObservation struct { + + // Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // +listType=set + Teams []*int64 `json:"teams,omitempty" tf:"teams,omitempty"` + + // Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // +listType=set + Users []*int64 `json:"users,omitempty" tf:"users,omitempty"` +} + +type ReviewersParameters struct { + + // Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // +kubebuilder:validation:Optional + // +listType=set + Teams []*int64 `json:"teams,omitempty" tf:"teams,omitempty"` + + // Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + // +kubebuilder:validation:Optional + // +listType=set + Users []*int64 `json:"users,omitempty" tf:"users,omitempty"` +} + +// EnvironmentSpec defines the desired state of Environment +type EnvironmentSpec struct { + v1.ResourceSpec `json:",inline"` + ForProvider EnvironmentParameters `json:"forProvider"` + // THIS IS A BETA FIELD. It will be honored + // unless the Management Policies feature flag is disabled. + // InitProvider holds the same fields as ForProvider, with the exception + // of Identifier and other resource reference fields. The fields that are + // in InitProvider are merged into ForProvider when the resource is created. + // The same fields are also added to the terraform ignore_changes hook, to + // avoid updating them after creation. This is useful for fields that are + // required on creation, but we do not desire to update them after creation, + // for example because of an external controller is managing them, like an + // autoscaler. + InitProvider EnvironmentInitParameters `json:"initProvider,omitempty"` +} + +// EnvironmentStatus defines the observed state of Environment. +type EnvironmentStatus struct { + v1.ResourceStatus `json:",inline"` + AtProvider EnvironmentObservation `json:"atProvider,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:storageversion + +// Environment is the Schema for the Environments API. Creates and manages environments for GitHub repositories +// +kubebuilder:printcolumn:name="SYNCED",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status" +// +kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status" +// +kubebuilder:printcolumn:name="EXTERNAL-NAME",type="string",JSONPath=".metadata.annotations.crossplane\\.io/external-name" +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:resource:scope=Cluster,categories={crossplane,managed,github} +type Environment struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + // +kubebuilder:validation:XValidation:rule="!('*' in self.managementPolicies || 'Create' in self.managementPolicies || 'Update' in self.managementPolicies) || has(self.forProvider.environment) || (has(self.initProvider) && has(self.initProvider.environment))",message="spec.forProvider.environment is a required parameter" + Spec EnvironmentSpec `json:"spec"` + Status EnvironmentStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// EnvironmentList contains a list of Environments +type EnvironmentList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Environment `json:"items"` +} + +// Repository type metadata. +var ( + Environment_Kind = "Environment" + Environment_GroupKind = schema.GroupKind{Group: CRDGroup, Kind: Environment_Kind}.String() + Environment_KindAPIVersion = Environment_Kind + "." + CRDGroupVersion.String() + Environment_GroupVersionKind = CRDGroupVersion.WithKind(Environment_Kind) +) + +func init() { + SchemeBuilder.Register(&Environment{}, &EnvironmentList{}) +} diff --git a/apis/repo/v1alpha1/zz_generated.conversion_hubs.go b/apis/repo/v1alpha1/zz_generated.conversion_hubs.go index d1722e1..d99c903 100755 --- a/apis/repo/v1alpha1/zz_generated.conversion_hubs.go +++ b/apis/repo/v1alpha1/zz_generated.conversion_hubs.go @@ -18,6 +18,9 @@ func (tr *DefaultBranch) Hub() {} // Hub marks this type as a conversion hub. func (tr *DeployKey) Hub() {} +// Hub marks this type as a conversion hub. +func (tr *Environment) Hub() {} + // Hub marks this type as a conversion hub. func (tr *PullRequest) Hub() {} diff --git a/apis/repo/v1alpha1/zz_generated.deepcopy.go b/apis/repo/v1alpha1/zz_generated.deepcopy.go index 17bc796..91f563b 100644 --- a/apis/repo/v1alpha1/zz_generated.deepcopy.go +++ b/apis/repo/v1alpha1/zz_generated.deepcopy.go @@ -1209,6 +1209,362 @@ func (in *DeployKeyStatus) DeepCopy() *DeployKeyStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentBranchPolicyInitParameters) DeepCopyInto(out *DeploymentBranchPolicyInitParameters) { + *out = *in + if in.CustomBranchPolicies != nil { + in, out := &in.CustomBranchPolicies, &out.CustomBranchPolicies + *out = new(bool) + **out = **in + } + if in.ProtectedBranches != nil { + in, out := &in.ProtectedBranches, &out.ProtectedBranches + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentBranchPolicyInitParameters. +func (in *DeploymentBranchPolicyInitParameters) DeepCopy() *DeploymentBranchPolicyInitParameters { + if in == nil { + return nil + } + out := new(DeploymentBranchPolicyInitParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentBranchPolicyObservation) DeepCopyInto(out *DeploymentBranchPolicyObservation) { + *out = *in + if in.CustomBranchPolicies != nil { + in, out := &in.CustomBranchPolicies, &out.CustomBranchPolicies + *out = new(bool) + **out = **in + } + if in.ProtectedBranches != nil { + in, out := &in.ProtectedBranches, &out.ProtectedBranches + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentBranchPolicyObservation. +func (in *DeploymentBranchPolicyObservation) DeepCopy() *DeploymentBranchPolicyObservation { + if in == nil { + return nil + } + out := new(DeploymentBranchPolicyObservation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentBranchPolicyParameters) DeepCopyInto(out *DeploymentBranchPolicyParameters) { + *out = *in + if in.CustomBranchPolicies != nil { + in, out := &in.CustomBranchPolicies, &out.CustomBranchPolicies + *out = new(bool) + **out = **in + } + if in.ProtectedBranches != nil { + in, out := &in.ProtectedBranches, &out.ProtectedBranches + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentBranchPolicyParameters. +func (in *DeploymentBranchPolicyParameters) DeepCopy() *DeploymentBranchPolicyParameters { + if in == nil { + return nil + } + out := new(DeploymentBranchPolicyParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Environment) DeepCopyInto(out *Environment) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Environment. +func (in *Environment) DeepCopy() *Environment { + if in == nil { + return nil + } + out := new(Environment) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Environment) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentInitParameters) DeepCopyInto(out *EnvironmentInitParameters) { + *out = *in + if in.CanAdminsBypass != nil { + in, out := &in.CanAdminsBypass, &out.CanAdminsBypass + *out = new(bool) + **out = **in + } + if in.DeploymentBranchPolicy != nil { + in, out := &in.DeploymentBranchPolicy, &out.DeploymentBranchPolicy + *out = make([]DeploymentBranchPolicyInitParameters, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Environment != nil { + in, out := &in.Environment, &out.Environment + *out = new(string) + **out = **in + } + if in.PreventSelfReview != nil { + in, out := &in.PreventSelfReview, &out.PreventSelfReview + *out = new(bool) + **out = **in + } + if in.Repository != nil { + in, out := &in.Repository, &out.Repository + *out = new(string) + **out = **in + } + if in.RepositoryRef != nil { + in, out := &in.RepositoryRef, &out.RepositoryRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.RepositorySelector != nil { + in, out := &in.RepositorySelector, &out.RepositorySelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } + if in.Reviewers != nil { + in, out := &in.Reviewers, &out.Reviewers + *out = make([]ReviewersInitParameters, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.WaitTimer != nil { + in, out := &in.WaitTimer, &out.WaitTimer + *out = new(int64) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentInitParameters. +func (in *EnvironmentInitParameters) DeepCopy() *EnvironmentInitParameters { + if in == nil { + return nil + } + out := new(EnvironmentInitParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentList) DeepCopyInto(out *EnvironmentList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Environment, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentList. +func (in *EnvironmentList) DeepCopy() *EnvironmentList { + if in == nil { + return nil + } + out := new(EnvironmentList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvironmentList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentObservation) DeepCopyInto(out *EnvironmentObservation) { + *out = *in + if in.CanAdminsBypass != nil { + in, out := &in.CanAdminsBypass, &out.CanAdminsBypass + *out = new(bool) + **out = **in + } + if in.DeploymentBranchPolicy != nil { + in, out := &in.DeploymentBranchPolicy, &out.DeploymentBranchPolicy + *out = make([]DeploymentBranchPolicyObservation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Environment != nil { + in, out := &in.Environment, &out.Environment + *out = new(string) + **out = **in + } + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.PreventSelfReview != nil { + in, out := &in.PreventSelfReview, &out.PreventSelfReview + *out = new(bool) + **out = **in + } + if in.Repository != nil { + in, out := &in.Repository, &out.Repository + *out = new(string) + **out = **in + } + if in.Reviewers != nil { + in, out := &in.Reviewers, &out.Reviewers + *out = make([]ReviewersObservation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.WaitTimer != nil { + in, out := &in.WaitTimer, &out.WaitTimer + *out = new(int64) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentObservation. +func (in *EnvironmentObservation) DeepCopy() *EnvironmentObservation { + if in == nil { + return nil + } + out := new(EnvironmentObservation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentParameters) DeepCopyInto(out *EnvironmentParameters) { + *out = *in + if in.CanAdminsBypass != nil { + in, out := &in.CanAdminsBypass, &out.CanAdminsBypass + *out = new(bool) + **out = **in + } + if in.DeploymentBranchPolicy != nil { + in, out := &in.DeploymentBranchPolicy, &out.DeploymentBranchPolicy + *out = make([]DeploymentBranchPolicyParameters, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Environment != nil { + in, out := &in.Environment, &out.Environment + *out = new(string) + **out = **in + } + if in.PreventSelfReview != nil { + in, out := &in.PreventSelfReview, &out.PreventSelfReview + *out = new(bool) + **out = **in + } + if in.Repository != nil { + in, out := &in.Repository, &out.Repository + *out = new(string) + **out = **in + } + if in.RepositoryRef != nil { + in, out := &in.RepositoryRef, &out.RepositoryRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.RepositorySelector != nil { + in, out := &in.RepositorySelector, &out.RepositorySelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } + if in.Reviewers != nil { + in, out := &in.Reviewers, &out.Reviewers + *out = make([]ReviewersParameters, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.WaitTimer != nil { + in, out := &in.WaitTimer, &out.WaitTimer + *out = new(int64) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentParameters. +func (in *EnvironmentParameters) DeepCopy() *EnvironmentParameters { + if in == nil { + return nil + } + out := new(EnvironmentParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentSpec) DeepCopyInto(out *EnvironmentSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) + in.ForProvider.DeepCopyInto(&out.ForProvider) + in.InitProvider.DeepCopyInto(&out.InitProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentSpec. +func (in *EnvironmentSpec) DeepCopy() *EnvironmentSpec { + if in == nil { + return nil + } + out := new(EnvironmentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentStatus) DeepCopyInto(out *EnvironmentStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) + in.AtProvider.DeepCopyInto(&out.AtProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentStatus. +func (in *EnvironmentStatus) DeepCopy() *EnvironmentStatus { + if in == nil { + return nil + } + out := new(EnvironmentStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PagesInitParameters) DeepCopyInto(out *PagesInitParameters) { *out = *in @@ -3857,6 +4213,117 @@ func (in *RestrictPushesParameters) DeepCopy() *RestrictPushesParameters { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReviewersInitParameters) DeepCopyInto(out *ReviewersInitParameters) { + *out = *in + if in.Teams != nil { + in, out := &in.Teams, &out.Teams + *out = make([]*int64, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(int64) + **out = **in + } + } + } + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]*int64, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(int64) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReviewersInitParameters. +func (in *ReviewersInitParameters) DeepCopy() *ReviewersInitParameters { + if in == nil { + return nil + } + out := new(ReviewersInitParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReviewersObservation) DeepCopyInto(out *ReviewersObservation) { + *out = *in + if in.Teams != nil { + in, out := &in.Teams, &out.Teams + *out = make([]*int64, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(int64) + **out = **in + } + } + } + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]*int64, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(int64) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReviewersObservation. +func (in *ReviewersObservation) DeepCopy() *ReviewersObservation { + if in == nil { + return nil + } + out := new(ReviewersObservation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReviewersParameters) DeepCopyInto(out *ReviewersParameters) { + *out = *in + if in.Teams != nil { + in, out := &in.Teams, &out.Teams + *out = make([]*int64, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(int64) + **out = **in + } + } + } + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]*int64, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(int64) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReviewersParameters. +func (in *ReviewersParameters) DeepCopy() *ReviewersParameters { + if in == nil { + return nil + } + out := new(ReviewersParameters) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretScanningInitParameters) DeepCopyInto(out *SecretScanningInitParameters) { *out = *in diff --git a/apis/repo/v1alpha1/zz_generated.managed.go b/apis/repo/v1alpha1/zz_generated.managed.go index 67d4d4b..997f8c6 100644 --- a/apis/repo/v1alpha1/zz_generated.managed.go +++ b/apis/repo/v1alpha1/zz_generated.managed.go @@ -247,6 +247,66 @@ func (mg *DeployKey) SetWriteConnectionSecretToReference(r *xpv1.SecretReference mg.Spec.WriteConnectionSecretToReference = r } +// GetCondition of this Environment. +func (mg *Environment) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this Environment. +func (mg *Environment) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetManagementPolicies of this Environment. +func (mg *Environment) GetManagementPolicies() xpv1.ManagementPolicies { + return mg.Spec.ManagementPolicies +} + +// GetProviderConfigReference of this Environment. +func (mg *Environment) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +// GetPublishConnectionDetailsTo of this Environment. +func (mg *Environment) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo { + return mg.Spec.PublishConnectionDetailsTo +} + +// GetWriteConnectionSecretToReference of this Environment. +func (mg *Environment) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this Environment. +func (mg *Environment) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this Environment. +func (mg *Environment) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetManagementPolicies of this Environment. +func (mg *Environment) SetManagementPolicies(r xpv1.ManagementPolicies) { + mg.Spec.ManagementPolicies = r +} + +// SetProviderConfigReference of this Environment. +func (mg *Environment) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +// SetPublishConnectionDetailsTo of this Environment. +func (mg *Environment) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) { + mg.Spec.PublishConnectionDetailsTo = r +} + +// SetWriteConnectionSecretToReference of this Environment. +func (mg *Environment) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} + // GetCondition of this PullRequest. func (mg *PullRequest) GetCondition(ct xpv1.ConditionType) xpv1.Condition { return mg.Status.GetCondition(ct) diff --git a/apis/repo/v1alpha1/zz_generated.managedlist.go b/apis/repo/v1alpha1/zz_generated.managedlist.go index 473a349..4e5c0f2 100644 --- a/apis/repo/v1alpha1/zz_generated.managedlist.go +++ b/apis/repo/v1alpha1/zz_generated.managedlist.go @@ -43,6 +43,15 @@ func (l *DeployKeyList) GetItems() []resource.Managed { return items } +// GetItems of this EnvironmentList. +func (l *EnvironmentList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} + // GetItems of this PullRequestList. func (l *PullRequestList) GetItems() []resource.Managed { items := make([]resource.Managed, len(l.Items)) diff --git a/apis/repo/v1alpha1/zz_generated.resolvers.go b/apis/repo/v1alpha1/zz_generated.resolvers.go index 954fd80..cbfae1b 100644 --- a/apis/repo/v1alpha1/zz_generated.resolvers.go +++ b/apis/repo/v1alpha1/zz_generated.resolvers.go @@ -181,6 +181,48 @@ func (mg *DeployKey) ResolveReferences(ctx context.Context, c client.Reader) err return nil } +// ResolveReferences of this Environment. +func (mg *Environment) ResolveReferences(ctx context.Context, c client.Reader) error { + r := reference.NewAPIResolver(c, mg) + + var rsp reference.ResolutionResponse + var err error + + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.Repository), + Extract: reference.ExternalName(), + Reference: mg.Spec.ForProvider.RepositoryRef, + Selector: mg.Spec.ForProvider.RepositorySelector, + To: reference.To{ + List: &RepositoryList{}, + Managed: &Repository{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.Repository") + } + mg.Spec.ForProvider.Repository = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.RepositoryRef = rsp.ResolvedReference + + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.Repository), + Extract: reference.ExternalName(), + Reference: mg.Spec.InitProvider.RepositoryRef, + Selector: mg.Spec.InitProvider.RepositorySelector, + To: reference.To{ + List: &RepositoryList{}, + Managed: &Repository{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.Repository") + } + mg.Spec.InitProvider.Repository = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.RepositoryRef = rsp.ResolvedReference + + return nil +} + // ResolveReferences of this PullRequest. func (mg *PullRequest) ResolveReferences(ctx context.Context, c client.Reader) error { r := reference.NewAPIResolver(c, mg) diff --git a/config/external_name.go b/config/external_name.go index eaed821..9b6760b 100644 --- a/config/external_name.go +++ b/config/external_name.go @@ -53,6 +53,8 @@ var terraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ "github_repository_autolink_reference": config.IdentifierFromProvider, // Can be imported using the following format: {{ repository }}:{{ username }}. "github_repository_collaborator": config.IdentifierFromProvider, + // Can be imported using the following format: {{ repository }}:{{ environment }} + "github_repository_environment": config.IdentifierFromProvider, } // cliReconciledExternalNameConfigs contains all external name configurations diff --git a/config/repository/config.go b/config/repository/config.go index 057abb7..ff48ead 100644 --- a/config/repository/config.go +++ b/config/repository/config.go @@ -28,4 +28,13 @@ func Configure(p *config.Provider) { return diff, nil } }) + + p.AddResourceConfigurator("github_repository_environment", func(r *config.Resource) { + r.ShortGroup = "repo" + + r.References["repository"] = config.Reference{ + Type: "Repository", + } + //TODO: Add user reference in when user resource exists. + }) } diff --git a/examples-generated/repo/v1alpha1/environment.yaml b/examples-generated/repo/v1alpha1/environment.yaml new file mode 100644 index 0000000..91140e7 --- /dev/null +++ b/examples-generated/repo/v1alpha1/environment.yaml @@ -0,0 +1,36 @@ +apiVersion: repo.github.upbound.io/v1alpha1 +kind: Environment +metadata: + annotations: + meta.upbound.io/example-id: repo/v1alpha1/environment + labels: + testing.upbound.io/example-name: example + name: example +spec: + forProvider: + deploymentBranchPolicy: + - customBranchPolicies: false + protectedBranches: true + environment: example + preventSelfReview: true + repositorySelector: + matchLabels: + testing.upbound.io/example-name: example + reviewers: + - users: + - ${data.github_user.current.id} + +--- + +apiVersion: repo.github.upbound.io/v1alpha1 +kind: Repository +metadata: + annotations: + meta.upbound.io/example-id: repo/v1alpha1/environment + labels: + testing.upbound.io/example-name: example + name: example +spec: + forProvider: + description: My awesome codebase + name: A Repository Project diff --git a/internal/controller/repo/environment/zz_controller.go b/internal/controller/repo/environment/zz_controller.go new file mode 100755 index 0000000..769dc78 --- /dev/null +++ b/internal/controller/repo/environment/zz_controller.go @@ -0,0 +1,91 @@ +/* +Copyright 2022 Upbound Inc. +*/ + +// Code generated by upjet. DO NOT EDIT. + +package environment + +import ( + "time" + + "github.com/crossplane/crossplane-runtime/pkg/connection" + "github.com/crossplane/crossplane-runtime/pkg/event" + "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + xpresource "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/crossplane/crossplane-runtime/pkg/statemetrics" + tjcontroller "github.com/crossplane/upjet/pkg/controller" + "github.com/crossplane/upjet/pkg/controller/handler" + "github.com/crossplane/upjet/pkg/metrics" + "github.com/pkg/errors" + ctrl "sigs.k8s.io/controller-runtime" + + v1alpha1 "github.com/crossplane-contrib/provider-upjet-github/apis/repo/v1alpha1" + features "github.com/crossplane-contrib/provider-upjet-github/internal/features" +) + +// Setup adds a controller that reconciles Environment managed resources. +func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { + name := managed.ControllerName(v1alpha1.Environment_GroupVersionKind.String()) + var initializers managed.InitializerChain + cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} + if o.SecretStoreConfigGVK != nil { + cps = append(cps, connection.NewDetailsManager(mgr.GetClient(), *o.SecretStoreConfigGVK, connection.WithTLSConfig(o.ESSOptions.TLSConfig))) + } + eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.Environment_GroupVersionKind))) + ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.Environment_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) + opts := []managed.ReconcilerOption{ + managed.WithExternalConnecter( + tjcontroller.NewTerraformPluginSDKAsyncConnector(mgr.GetClient(), o.OperationTrackerStore, o.SetupFn, o.Provider.Resources["github_repository_environment"], + tjcontroller.WithTerraformPluginSDKAsyncLogger(o.Logger), + tjcontroller.WithTerraformPluginSDKAsyncConnectorEventHandler(eventHandler), + tjcontroller.WithTerraformPluginSDKAsyncCallbackProvider(ac), + tjcontroller.WithTerraformPluginSDKAsyncMetricRecorder(metrics.NewMetricRecorder(v1alpha1.Environment_GroupVersionKind, mgr, o.PollInterval)), + tjcontroller.WithTerraformPluginSDKAsyncManagementPolicies(o.Features.Enabled(features.EnableBetaManagementPolicies)))), + managed.WithLogger(o.Logger.WithValues("controller", name)), + managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), + managed.WithFinalizer(tjcontroller.NewOperationTrackerFinalizer(o.OperationTrackerStore, xpresource.NewAPIFinalizer(mgr.GetClient(), managed.FinalizerName))), + managed.WithTimeout(3 * time.Minute), + managed.WithInitializers(initializers), + managed.WithConnectionPublishers(cps...), + managed.WithPollInterval(o.PollInterval), + } + if o.PollJitter != 0 { + opts = append(opts, managed.WithPollJitterHook(o.PollJitter)) + } + if o.Features.Enabled(features.EnableBetaManagementPolicies) { + opts = append(opts, managed.WithManagementPolicies()) + } + if o.MetricOptions != nil { + opts = append(opts, managed.WithMetricRecorder(o.MetricOptions.MRMetrics)) + } + + // register webhooks for the kind v1alpha1.Environment + // if they're enabled. + if o.StartWebhooks { + if err := ctrl.NewWebhookManagedBy(mgr). + For(&v1alpha1.Environment{}). + Complete(); err != nil { + return errors.Wrap(err, "cannot register webhook for the kind v1alpha1.Environment") + } + } + + if o.MetricOptions != nil && o.MetricOptions.MRStateMetrics != nil { + stateMetricsRecorder := statemetrics.NewMRStateRecorder( + mgr.GetClient(), o.Logger, o.MetricOptions.MRStateMetrics, &v1alpha1.EnvironmentList{}, o.MetricOptions.PollStateMetricInterval, + ) + if err := mgr.Add(stateMetricsRecorder); err != nil { + return errors.Wrap(err, "cannot register MR state metrics recorder for kind v1alpha1.EnvironmentList") + } + } + + r := managed.NewReconciler(mgr, xpresource.ManagedKind(v1alpha1.Environment_GroupVersionKind), opts...) + + return ctrl.NewControllerManagedBy(mgr). + Named(name). + WithOptions(o.ForControllerRuntime()). + WithEventFilter(xpresource.DesiredStateChanged()). + Watches(&v1alpha1.Environment{}, eventHandler). + Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) +} diff --git a/internal/controller/zz_setup.go b/internal/controller/zz_setup.go index 7d1b2fd..e16d420 100755 --- a/internal/controller/zz_setup.go +++ b/internal/controller/zz_setup.go @@ -18,6 +18,7 @@ import ( branchprotection "github.com/crossplane-contrib/provider-upjet-github/internal/controller/repo/branchprotection" defaultbranch "github.com/crossplane-contrib/provider-upjet-github/internal/controller/repo/defaultbranch" deploykey "github.com/crossplane-contrib/provider-upjet-github/internal/controller/repo/deploykey" + environment "github.com/crossplane-contrib/provider-upjet-github/internal/controller/repo/environment" pullrequest "github.com/crossplane-contrib/provider-upjet-github/internal/controller/repo/pullrequest" repository "github.com/crossplane-contrib/provider-upjet-github/internal/controller/repo/repository" repositoryautolinkreference "github.com/crossplane-contrib/provider-upjet-github/internal/controller/repo/repositoryautolinkreference" @@ -46,6 +47,7 @@ func Setup(mgr ctrl.Manager, o controller.Options) error { branchprotection.Setup, defaultbranch.Setup, deploykey.Setup, + environment.Setup, pullrequest.Setup, repository.Setup, repositoryautolinkreference.Setup, diff --git a/package/crds/repo.github.upbound.io_environments.yaml b/package/crds/repo.github.upbound.io_environments.yaml new file mode 100644 index 0000000..c224649 --- /dev/null +++ b/package/crds/repo.github.upbound.io_environments.yaml @@ -0,0 +1,677 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: environments.repo.github.upbound.io +spec: + group: repo.github.upbound.io + names: + categories: + - crossplane + - managed + - github + kind: Environment + listKind: EnvironmentList + plural: environments + singular: environment + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Synced')].status + name: SYNCED + type: string + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: READY + type: string + - jsonPath: .metadata.annotations.crossplane\.io/external-name + name: EXTERNAL-NAME + type: string + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: Environment is the Schema for the Environments API. Creates and + manages environments for GitHub repositories + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: EnvironmentSpec defines the desired state of Environment + properties: + deletionPolicy: + default: Delete + description: |- + DeletionPolicy specifies what will happen to the underlying external + when this managed resource is deleted - either "Delete" or "Orphan" the + external resource. + This field is planned to be deprecated in favor of the ManagementPolicies + field in a future release. Currently, both could be set independently and + non-default values would be honored if the feature flag is enabled. + See the design doc for more information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 + enum: + - Orphan + - Delete + type: string + forProvider: + properties: + canAdminsBypass: + description: |- + Can repository admins bypass the environment protections. Defaults to true. + Can Admins bypass deployment protections + type: boolean + deploymentBranchPolicy: + description: The deployment branch policy configuration + items: + properties: + customBranchPolicies: + description: |- + Whether only branches that match the specified name patterns can deploy to this environment. + Whether only branches that match the specified name patterns can deploy to this environment. + type: boolean + protectedBranches: + description: |- + Whether only branches with branch protection rules can deploy to this environment. + Whether only branches with branch protection rules can deploy to this environment. + type: boolean + type: object + type: array + environment: + description: |- + The name of the environment. + The name of the environment. + type: string + preventSelfReview: + description: |- + Whether or not a user who created the job is prevented from approving their own job. Defaults to false. + Prevent users from approving workflows runs that they triggered. + type: boolean + repository: + description: |- + The repository of the environment. + The repository of the environment. + type: string + repositoryRef: + description: Reference to a Repository to populate repository. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + repositorySelector: + description: Selector for a Repository to populate repository. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object + reviewers: + description: The environment reviewers configuration. + items: + properties: + teams: + description: |- + Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: set + users: + description: |- + Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: set + type: object + type: array + waitTimer: + description: |- + Amount of time to delay a job after the job is initially triggered. + Amount of time to delay a job after the job is initially triggered. + format: int64 + type: integer + type: object + initProvider: + description: |- + THIS IS A BETA FIELD. It will be honored + unless the Management Policies feature flag is disabled. + InitProvider holds the same fields as ForProvider, with the exception + of Identifier and other resource reference fields. The fields that are + in InitProvider are merged into ForProvider when the resource is created. + The same fields are also added to the terraform ignore_changes hook, to + avoid updating them after creation. This is useful for fields that are + required on creation, but we do not desire to update them after creation, + for example because of an external controller is managing them, like an + autoscaler. + properties: + canAdminsBypass: + description: |- + Can repository admins bypass the environment protections. Defaults to true. + Can Admins bypass deployment protections + type: boolean + deploymentBranchPolicy: + description: The deployment branch policy configuration + items: + properties: + customBranchPolicies: + description: |- + Whether only branches that match the specified name patterns can deploy to this environment. + Whether only branches that match the specified name patterns can deploy to this environment. + type: boolean + protectedBranches: + description: |- + Whether only branches with branch protection rules can deploy to this environment. + Whether only branches with branch protection rules can deploy to this environment. + type: boolean + type: object + type: array + environment: + description: |- + The name of the environment. + The name of the environment. + type: string + preventSelfReview: + description: |- + Whether or not a user who created the job is prevented from approving their own job. Defaults to false. + Prevent users from approving workflows runs that they triggered. + type: boolean + repository: + description: |- + The repository of the environment. + The repository of the environment. + type: string + repositoryRef: + description: Reference to a Repository to populate repository. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + repositorySelector: + description: Selector for a Repository to populate repository. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object + reviewers: + description: The environment reviewers configuration. + items: + properties: + teams: + description: |- + Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: set + users: + description: |- + Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: set + type: object + type: array + waitTimer: + description: |- + Amount of time to delay a job after the job is initially triggered. + Amount of time to delay a job after the job is initially triggered. + format: int64 + type: integer + type: object + managementPolicies: + default: + - '*' + description: |- + THIS IS A BETA FIELD. It is on by default but can be opted out + through a Crossplane feature flag. + ManagementPolicies specify the array of actions Crossplane is allowed to + take on the managed and external resources. + This field is planned to replace the DeletionPolicy field in a future + release. Currently, both could be set independently and non-default + values would be honored if the feature flag is enabled. If both are + custom, the DeletionPolicy field will be ignored. + See the design doc for more information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 + and this one: https://github.com/crossplane/crossplane/blob/444267e84783136daa93568b364a5f01228cacbe/design/one-pager-ignore-changes.md + items: + description: |- + A ManagementAction represents an action that the Crossplane controllers + can take on an external resource. + enum: + - Observe + - Create + - Update + - Delete + - LateInitialize + - '*' + type: string + type: array + providerConfigRef: + default: + name: default + description: |- + ProviderConfigReference specifies how the provider that will be used to + create, observe, update, and delete this managed resource should be + configured. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + publishConnectionDetailsTo: + description: |- + PublishConnectionDetailsTo specifies the connection secret config which + contains a name, metadata and a reference to secret store config to + which any connection details for this managed resource should be written. + Connection details frequently include the endpoint, username, + and password required to connect to the managed resource. + properties: + configRef: + default: + name: default + description: |- + SecretStoreConfigRef specifies which secret store config should be used + for this ConnectionSecret. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + metadata: + description: Metadata is the metadata for connection secret. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations are the annotations to be added to connection secret. + - For Kubernetes secrets, this will be used as "metadata.annotations". + - It is up to Secret Store implementation for others store types. + type: object + labels: + additionalProperties: + type: string + description: |- + Labels are the labels/tags to be added to connection secret. + - For Kubernetes secrets, this will be used as "metadata.labels". + - It is up to Secret Store implementation for others store types. + type: object + type: + description: |- + Type is the SecretType for the connection secret. + - Only valid for Kubernetes Secret Stores. + type: string + type: object + name: + description: Name is the name of the connection secret. + type: string + required: + - name + type: object + writeConnectionSecretToRef: + description: |- + WriteConnectionSecretToReference specifies the namespace and name of a + Secret to which any connection details for this managed resource should + be written. Connection details frequently include the endpoint, username, + and password required to connect to the managed resource. + This field is planned to be replaced in a future release in favor of + PublishConnectionDetailsTo. Currently, both could be set independently + and connection details would be published to both without affecting + each other. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + required: + - forProvider + type: object + x-kubernetes-validations: + - message: spec.forProvider.environment is a required parameter + rule: '!(''*'' in self.managementPolicies || ''Create'' in self.managementPolicies + || ''Update'' in self.managementPolicies) || has(self.forProvider.environment) + || (has(self.initProvider) && has(self.initProvider.environment))' + status: + description: EnvironmentStatus defines the observed state of Environment. + properties: + atProvider: + properties: + canAdminsBypass: + description: |- + Can repository admins bypass the environment protections. Defaults to true. + Can Admins bypass deployment protections + type: boolean + deploymentBranchPolicy: + description: The deployment branch policy configuration + items: + properties: + customBranchPolicies: + description: |- + Whether only branches that match the specified name patterns can deploy to this environment. + Whether only branches that match the specified name patterns can deploy to this environment. + type: boolean + protectedBranches: + description: |- + Whether only branches with branch protection rules can deploy to this environment. + Whether only branches with branch protection rules can deploy to this environment. + type: boolean + type: object + type: array + environment: + description: |- + The name of the environment. + The name of the environment. + type: string + id: + type: string + preventSelfReview: + description: |- + Whether or not a user who created the job is prevented from approving their own job. Defaults to false. + Prevent users from approving workflows runs that they triggered. + type: boolean + repository: + description: |- + The repository of the environment. + The repository of the environment. + type: string + reviewers: + description: The environment reviewers configuration. + items: + properties: + teams: + description: |- + Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: set + users: + description: |- + Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: set + type: object + type: array + waitTimer: + description: |- + Amount of time to delay a job after the job is initially triggered. + Amount of time to delay a job after the job is initially triggered. + format: int64 + type: integer + type: object + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time this condition transitioned from one + status to another. + format: date-time + type: string + message: + description: |- + A Message containing details about this condition's last transition from + one status to another, if any. + type: string + observedGeneration: + description: |- + ObservedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + type: integer + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: |- + Type of this condition. At most one of each condition type may apply to + a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + observedGeneration: + description: |- + ObservedGeneration is the latest metadata.generation + which resulted in either a ready state, or stalled due to error + it can not recover from without human intervention. + format: int64 + type: integer + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} From 2577d09c2cf1ae362a3f624aefe91ab8fa868e52 Mon Sep 17 00:00:00 2001 From: Atze de Vries Date: Mon, 9 Dec 2024 14:43:17 +0100 Subject: [PATCH 2/4] bump actions in ci --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3dc0e9c..506f30c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: with: submodules: true - name: Setup Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} - name: Find the Go Build Cache @@ -61,7 +61,7 @@ jobs: # We could run 'make lint' but we prefer this action because it leaves # 'annotations' (i.e. it comments on PRs to point out linter violations). - name: Lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: version: ${{ env.GOLANGCI_VERSION }} check-diff: From 7fde8bab0061d3396b9e94b4f7fb0bd5785e064b Mon Sep 17 00:00:00 2001 From: Atze de Vries Date: Mon, 9 Dec 2024 14:46:37 +0100 Subject: [PATCH 3/4] fix linting --- config/repository/config.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/config/repository/config.go b/config/repository/config.go index ff48ead..ff14af3 100644 --- a/config/repository/config.go +++ b/config/repository/config.go @@ -29,12 +29,12 @@ func Configure(p *config.Provider) { } }) - p.AddResourceConfigurator("github_repository_environment", func(r *config.Resource) { - r.ShortGroup = "repo" + p.AddResourceConfigurator("github_repository_environment", func(r *config.Resource) { + r.ShortGroup = "repo" - r.References["repository"] = config.Reference{ - Type: "Repository", - } - //TODO: Add user reference in when user resource exists. - }) + r.References["repository"] = config.Reference{ + Type: "Repository", + } + //TODO: Add user reference in when user resource exists. + }) } From d938645c7c0df4756bf311eb8cdfe8b622307504 Mon Sep 17 00:00:00 2001 From: Atze de Vries Date: Mon, 9 Dec 2024 14:49:29 +0100 Subject: [PATCH 4/4] more formatting --- config/external_name.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/external_name.go b/config/external_name.go index 9b6760b..cfea638 100644 --- a/config/external_name.go +++ b/config/external_name.go @@ -53,8 +53,8 @@ var terraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ "github_repository_autolink_reference": config.IdentifierFromProvider, // Can be imported using the following format: {{ repository }}:{{ username }}. "github_repository_collaborator": config.IdentifierFromProvider, - // Can be imported using the following format: {{ repository }}:{{ environment }} - "github_repository_environment": config.IdentifierFromProvider, + // Can be imported using the following format: {{ repository }}:{{ environment }} + "github_repository_environment": config.IdentifierFromProvider, } // cliReconciledExternalNameConfigs contains all external name configurations