diff --git a/api/v1beta2/gitrepository_types.go b/api/v1beta2/gitrepository_types.go index 07091280..e02cf005 100644 --- a/api/v1beta2/gitrepository_types.go +++ b/api/v1beta2/gitrepository_types.go @@ -29,85 +29,116 @@ const ( // GitRepositoryKind is the string representation of a GitRepository. GitRepositoryKind = "GitRepository" - // GoGitImplementation represents the go-git Git implementation kind. + // GoGitImplementation for performing Git operations using go-git. GoGitImplementation = "go-git" - // LibGit2Implementation represents the git2go Git implementation kind. + // LibGit2Implementation for performing Git operations using libgit2. LibGit2Implementation = "libgit2" ) const ( - // IncludeUnavailableCondition indicates one of the includes is not available. For example, because it does not - // exist, or does not have an Artifact. - // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. + // IncludeUnavailableCondition indicates one of the includes is not + // available. For example, because it does not exist, or does not have an + // Artifact. + // This is a "negative polarity" or "abnormal-true" type, and is only + // present on the resource if it is True. IncludeUnavailableCondition string = "IncludeUnavailable" ) -// GitRepositorySpec defines the desired state of a Git repository. +// GitRepositorySpec specifies the required configuration to produce an +// Artifact for a Git repository. type GitRepositorySpec struct { - // The repository URL, can be a HTTP/S or SSH address. + // URL specifies the Git repository URL, can be an HTTP/S or SSH address. // +kubebuilder:validation:Pattern="^(http|https|ssh)://" // +required URL string `json:"url"` - // The secret name containing the Git credentials. - // For HTTPS repositories the secret must contain username and password fields. - // For SSH repositories the secret must contain 'identity', 'identity.pub' and 'known_hosts' fields. + // SecretRef specifies the Secret containing authentication credentials for + // the GitRepository. + // For HTTPS repositories the Secret must contain 'username' and 'password' + // fields. + // For SSH repositories the Secret must contain 'identity', 'identity.pub' + // and 'known_hosts' fields. // +optional SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"` - // The interval at which to check for repository updates. + // Interval at which to check the GitRepository for updates. // +required Interval metav1.Duration `json:"interval"` - // The timeout for remote Git operations like cloning, defaults to 60s. + // Timeout for Git operations like cloning, defaults to 60s. // +kubebuilder:default="60s" // +optional Timeout *metav1.Duration `json:"timeout,omitempty"` - // The Git reference to checkout and monitor for changes, defaults to - // master branch. + // Reference specifies the Git reference to resolve and monitor for + // changes, defaults to the 'master' branch. // +optional Reference *GitRepositoryRef `json:"ref,omitempty"` - // Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to. + // Verification specifies the configuration to verify the Git commit + // signature(s). // +optional Verification *GitRepositoryVerification `json:"verify,omitempty"` - // Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore). - // If not provided, a default will be used, consult the documentation for your version to find out what those are. + // Ignore overrides the set of excluded patterns in the .sourceignore format + // (which is the same as .gitignore). If not provided, a default will be used, + // consult the documentation for your version to find out what those are. // +optional Ignore *string `json:"ignore,omitempty"` - // Suspend tells the controller to suspend the reconciliation of this source. - // This flag tells the controller to suspend the reconciliation of this source. + // Suspend tells the controller to suspend the reconciliation of this + // GitRepository. // +optional Suspend bool `json:"suspend,omitempty"` - // Determines which git client library to use. - // Defaults to go-git, valid values are ('go-git', 'libgit2'). + // GitImplementation specifies which Git client library implementation to + // use. Defaults to 'go-git', valid values are ('go-git', 'libgit2'). // +kubebuilder:validation:Enum=go-git;libgit2 // +kubebuilder:default:=go-git // +optional GitImplementation string `json:"gitImplementation,omitempty"` - // When enabled, after the clone is created, initializes all submodules within, using their default settings. + // RecurseSubmodules enables the initialization of all submodules within + // the GitRepository as cloned from the URL, using their default settings. // This option is available only when using the 'go-git' GitImplementation. // +optional RecurseSubmodules bool `json:"recurseSubmodules,omitempty"` - // Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for - // this resource. + // Include specifies a list of GitRepository resources which Artifacts + // should be included in the Artifact produced for this GitRepository. Include []GitRepositoryInclude `json:"include,omitempty"` - // AccessFrom defines an Access Control List for allowing cross-namespace references to this object. + // AccessFrom specifies an Access Control List for allowing cross-namespace + // references to this object. // +optional AccessFrom *acl.AccessFrom `json:"accessFrom,omitempty"` } +// GitRepositoryInclude specifies a reference to a GitRepository which Artifact +// (sub-)contents must be included, and where they should be placed. +type GitRepositoryInclude struct { + // GitRepositoryRef specifies the GitRepository which Artifact contents + // must be included. + GitRepositoryRef meta.LocalObjectReference `json:"repository"` + + // FromPath specifies the path to copy contents from, defaults to the root + // of the Artifact. + // +optional + FromPath string `json:"fromPath"` + + // ToPath specifies the path to copy contents to, defaults to the name of + // the GitRepositoryRef. + // +optional + ToPath string `json:"toPath"` +} + +// GetFromPath returns the specified FromPath. func (in *GitRepositoryInclude) GetFromPath() string { return in.FromPath } +// GetToPath returns the specified ToPath, falling back to the name of the +// GitRepositoryRef. func (in *GitRepositoryInclude) GetToPath() string { if in.ToPath == "" { return in.GitRepositoryRef.Name @@ -115,52 +146,48 @@ func (in *GitRepositoryInclude) GetToPath() string { return in.ToPath } -// GitRepositoryInclude defines a source with a from and to path. -type GitRepositoryInclude struct { - // Reference to a GitRepository to include. - GitRepositoryRef meta.LocalObjectReference `json:"repository"` - - // The path to copy contents from, defaults to the root directory. - // +optional - FromPath string `json:"fromPath"` - - // The path to copy contents to, defaults to the name of the source ref. - // +optional - ToPath string `json:"toPath"` -} - -// GitRepositoryRef defines the Git ref used for pull and checkout operations. +// GitRepositoryRef specifies the Git reference to resolve and checkout. type GitRepositoryRef struct { - // The Git branch to checkout, defaults to master. + // Branch to check out, defaults to 'master' if no other field is defined. + // + // When GitRepositorySpec.GitImplementation is set to 'go-git', a shallow + // clone of the specified branch is performed. // +optional Branch string `json:"branch,omitempty"` - // The Git tag to checkout, takes precedence over Branch. + // Tag to check out, takes precedence over Branch. // +optional Tag string `json:"tag,omitempty"` - // The Git tag semver expression, takes precedence over Tag. + // SemVer tag expression to check out, takes precedence over Tag. // +optional SemVer string `json:"semver,omitempty"` - // The Git commit SHA to checkout, if specified Tag filters will be ignored. + // Commit SHA to check out, takes precedence over all reference fields. + // + // When GitRepositorySpec.GitImplementation is set to 'go-git', this can be + // combined with Branch to shallow clone the branch, in which the commit is + // expected to exist. // +optional Commit string `json:"commit,omitempty"` } -// GitRepositoryVerification defines the OpenPGP signature verification process. +// GitRepositoryVerification specifies the Git commit signature verification +// strategy. type GitRepositoryVerification struct { - // Mode describes what Git object should be verified, currently ('head'). + // Mode specifies what Git object should be verified, currently ('head'). // +kubebuilder:validation:Enum=head Mode string `json:"mode"` - // SecretRef containing the public keys of all trusted Git authors. + // SecretRef specifies the Secret containing the public keys of trusted Git + // authors. SecretRef meta.LocalObjectReference `json:"secretRef,omitempty"` } -// GitRepositoryStatus defines the observed state of a Git repository. +// GitRepositoryStatus records the observed state of a Git repository. type GitRepositoryStatus struct { - // ObservedGeneration is the last observed generation. + // ObservedGeneration is the last observed generation of the GitRepository + // object. // +optional ObservedGeneration int64 `json:"observedGeneration,omitempty"` @@ -168,15 +195,18 @@ type GitRepositoryStatus struct { // +optional Conditions []metav1.Condition `json:"conditions,omitempty"` - // URL is the fetch link for the artifact output of the last repository sync. + // URL is the dynamic fetch link for the latest Artifact. + // It is provided on a "best effort" basis, and using the precise + // GitRepositoryStatus.Artifact data is recommended. // +optional URL string `json:"url,omitempty"` - // Artifact represents the output of the last successful repository sync. + // Artifact represents the last successful GitRepository reconciliation. // +optional Artifact *Artifact `json:"artifact,omitempty"` - // IncludedArtifacts represents the included artifacts from the last successful repository sync. + // IncludedArtifacts contains a list of the last successfully included + // Artifacts as instructed by GitRepositorySpec.Include. // +optional IncludedArtifacts []*Artifact `json:"includedArtifacts,omitempty"` @@ -184,10 +214,12 @@ type GitRepositoryStatus struct { } const ( - // GitOperationSucceedReason represents the fact that the git clone, pull and checkout operations succeeded. - GitOperationSucceedReason string = "GitOperationSucceed" + // GitOperationSucceedReason signals that a Git operation (e.g. clone, + // checkout, etc.) succeeded. + GitOperationSucceedReason string = "GitOperationSucceeded" - // GitOperationFailedReason represents the fact that the git clone, pull or checkout operations failed. + // GitOperationFailedReason signals that a Git operation (e.g. clone, + // checkout, etc.) failed. GitOperationFailedReason string = "GitOperationFailed" ) @@ -201,28 +233,18 @@ func (in *GitRepository) SetConditions(conditions []metav1.Condition) { in.Status.Conditions = conditions } -// GetRequeueAfter returns the duration after which the source must be reconciled again. +// GetRequeueAfter returns the duration after which the GitRepository must be +// reconciled again. func (in GitRepository) GetRequeueAfter() time.Duration { return in.Spec.Interval.Duration } -// GetInterval returns the interval at which the source is reconciled. -// Deprecated: use GetRequeueAfter instead. -func (in GitRepository) GetInterval() metav1.Duration { - return in.Spec.Interval -} - -// GetArtifact returns the latest artifact from the source if present in the status sub-resource. +// GetArtifact returns the latest Artifact from the GitRepository if present in +// the status sub-resource. func (in *GitRepository) GetArtifact() *Artifact { return in.Status.Artifact } -// GetStatusConditions returns a pointer to the Status.Conditions slice. -// Deprecated: use GetConditions instead. -func (in *GitRepository) GetStatusConditions() *[]metav1.Condition { - return &in.Status.Conditions -} - // +genclient // +genclient:Namespaced // +kubebuilder:storageversion @@ -234,7 +256,7 @@ func (in *GitRepository) GetStatusConditions() *[]metav1.Condition { // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" -// GitRepository is the Schema for the gitrepositories API +// GitRepository is the Schema for the gitrepositories API. type GitRepository struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -244,7 +266,7 @@ type GitRepository struct { Status GitRepositoryStatus `json:"status,omitempty"` } -// GitRepositoryList contains a list of GitRepository +// GitRepositoryList contains a list of GitRepository objects. // +kubebuilder:object:root=true type GitRepositoryList struct { metav1.TypeMeta `json:",inline"` diff --git a/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml b/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml index 2c0291fb..334c1def 100644 --- a/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml +++ b/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml @@ -365,7 +365,7 @@ spec: name: v1beta2 schema: openAPIV3Schema: - description: GitRepository is the Schema for the gitrepositories API + description: GitRepository is the Schema for the gitrepositories API. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -380,10 +380,11 @@ spec: metadata: type: object spec: - description: GitRepositorySpec defines the desired state of a Git repository. + description: GitRepositorySpec specifies the required configuration to + produce an Artifact for a Git repository. properties: accessFrom: - description: AccessFrom defines an Access Control List for allowing + description: AccessFrom specifies an Access Control List for allowing cross-namespace references to this object. properties: namespaceSelectors: @@ -411,8 +412,9 @@ spec: type: object gitImplementation: default: go-git - description: Determines which git client library to use. Defaults - to go-git, valid values are ('go-git', 'libgit2'). + description: GitImplementation specifies which Git client library + implementation to use. Defaults to 'go-git', valid values are ('go-git', + 'libgit2'). enum: - go-git - libgit2 @@ -424,18 +426,20 @@ spec: to find out what those are. type: string include: - description: Include defines a list of GitRepository resources which - artifacts should be included in the artifact produced for this resource. + description: Include specifies a list of GitRepository resources which + Artifacts should be included in the Artifact produced for this GitRepository. items: - description: GitRepositoryInclude defines a source with a from and - to path. + description: GitRepositoryInclude specifies a reference to a GitRepository + which Artifact (sub-)contents must be included, and where they + should be placed. properties: fromPath: - description: The path to copy contents from, defaults to the - root directory. + description: FromPath specifies the path to copy contents from, + defaults to the root of the Artifact. type: string repository: - description: Reference to a GitRepository to include. + description: GitRepositoryRef specifies the GitRepository which + Artifact contents must be included. properties: name: description: Name of the referent. @@ -444,45 +448,52 @@ spec: - name type: object toPath: - description: The path to copy contents to, defaults to the name - of the source ref. + description: ToPath specifies the path to copy contents to, + defaults to the name of the GitRepositoryRef. type: string required: - repository type: object type: array interval: - description: The interval at which to check for repository updates. + description: Interval at which to check the GitRepository for updates. type: string recurseSubmodules: - description: When enabled, after the clone is created, initializes - all submodules within, using their default settings. This option - is available only when using the 'go-git' GitImplementation. + description: RecurseSubmodules enables the initialization of all submodules + within the GitRepository as cloned from the URL, using their default + settings. This option is available only when using the 'go-git' + GitImplementation. type: boolean ref: - description: The Git reference to checkout and monitor for changes, - defaults to master branch. + description: Reference specifies the Git reference to resolve and + monitor for changes, defaults to the 'master' branch. properties: branch: - description: The Git branch to checkout, defaults to master. + description: "Branch to check out, defaults to 'master' if no + other field is defined. \n When GitRepositorySpec.GitImplementation + is set to 'go-git', a shallow clone of the specified branch + is performed." type: string commit: - description: The Git commit SHA to checkout, if specified Tag - filters will be ignored. + description: "Commit SHA to check out, takes precedence over all + reference fields. \n When GitRepositorySpec.GitImplementation + is set to 'go-git', this can be combined with Branch to shallow + clone the branch, in which the commit is expected to exist." type: string semver: - description: The Git tag semver expression, takes precedence over - Tag. + description: SemVer tag expression to check out, takes precedence + over Tag. type: string tag: - description: The Git tag to checkout, takes precedence over Branch. + description: Tag to check out, takes precedence over Branch. type: string type: object secretRef: - description: The secret name containing the Git credentials. For HTTPS - repositories the secret must contain username and password fields. - For SSH repositories the secret must contain 'identity', 'identity.pub' - and 'known_hosts' fields. + description: SecretRef specifies the Secret containing authentication + credentials for the GitRepository. For HTTPS repositories the Secret + must contain 'username' and 'password' fields. For SSH repositories + the Secret must contain 'identity', 'identity.pub' and 'known_hosts' + fields. properties: name: description: Name of the referent. @@ -492,31 +503,31 @@ spec: type: object suspend: description: Suspend tells the controller to suspend the reconciliation - of this source. This flag tells the controller to suspend the reconciliation - of this source. + of this GitRepository. type: boolean timeout: default: 60s - description: The timeout for remote Git operations like cloning, defaults - to 60s. + description: Timeout for Git operations like cloning, defaults to + 60s. type: string url: - description: The repository URL, can be a HTTP/S or SSH address. + description: URL specifies the Git repository URL, can be an HTTP/S + or SSH address. pattern: ^(http|https|ssh):// type: string verify: - description: Verification defines the configuration to verify the - OpenPGP signature for the Git commit HEAD points to. + description: Verification specifies the configuration to verify the + Git commit signature(s). properties: mode: - description: Mode describes what Git object should be verified, + description: Mode specifies what Git object should be verified, currently ('head'). enum: - head type: string secretRef: - description: SecretRef containing the public keys of all trusted - Git authors. + description: SecretRef specifies the Secret containing the public + keys of trusted Git authors. properties: name: description: Name of the referent. @@ -534,11 +545,11 @@ spec: status: default: observedGeneration: -1 - description: GitRepositoryStatus defines the observed state of a Git repository. + description: GitRepositoryStatus records the observed state of a Git repository. properties: artifact: - description: Artifact represents the output of the last successful - repository sync. + description: Artifact represents the last successful GitRepository + reconciliation. properties: checksum: description: Checksum is the SHA256 checksum of the Artifact file. @@ -639,8 +650,8 @@ spec: type: object type: array includedArtifacts: - description: IncludedArtifacts represents the included artifacts from - the last successful repository sync. + description: IncludedArtifacts contains a list of the last successfully + included Artifacts as instructed by GitRepositorySpec.Include. items: description: Artifact represents the output of a Source reconciliation. properties: @@ -681,12 +692,14 @@ spec: be detected. type: string observedGeneration: - description: ObservedGeneration is the last observed generation. + description: ObservedGeneration is the last observed generation of + the GitRepository object. format: int64 type: integer url: - description: URL is the fetch link for the artifact output of the - last repository sync. + description: URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise GitRepositoryStatus.Artifact + data is recommended. type: string type: object type: object diff --git a/controllers/gitrepository_controller.go b/controllers/gitrepository_controller.go index dd67041d..4f7606ef 100644 --- a/controllers/gitrepository_controller.go +++ b/controllers/gitrepository_controller.go @@ -53,9 +53,9 @@ import ( "github.com/fluxcd/source-controller/pkg/sourceignore" ) -// gitRepoReadyConditions contains all the conditions information needed -// for GitRepository Ready status conditions summary calculation. -var gitRepoReadyConditions = summarize.Conditions{ +// gitRepositoryReadyCondition contains the information required to summarize a +// v1beta2.GitRepository Ready Condition. +var gitRepositoryReadyCondition = summarize.Conditions{ Target: meta.ReadyCondition, Owned: []string{ sourcev1.SourceVerifiedCondition, @@ -88,7 +88,7 @@ var gitRepoReadyConditions = summarize.Conditions{ // +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories/finalizers,verbs=get;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=events,verbs=create;patch -// GitRepositoryReconciler reconciles a GitRepository object +// GitRepositoryReconciler reconciles a v1beta2.GitRepository object. type GitRepositoryReconciler struct { client.Client kuberecorder.EventRecorder @@ -104,9 +104,9 @@ type GitRepositoryReconcilerOptions struct { DependencyRequeueInterval time.Duration } -// gitRepoReconcilerFunc is the function type for all the Git repository -// reconciler functions. -type gitRepoReconcilerFunc func(ctx context.Context, obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error) +// gitRepositoryReconcileFunc is the function type for all the +// v1beta2.GitRepository (sub)reconcile functions. +type gitRepositoryReconcileFunc func(ctx context.Context, obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error) func (r *GitRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error { return r.SetupWithManagerAndOptions(mgr, GitRepositoryReconcilerOptions{}) @@ -156,7 +156,7 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques defer func() { summarizeHelper := summarize.NewHelper(r.EventRecorder, patchHelper) summarizeOpts := []summarize.Option{ - summarize.WithConditions(gitRepoReadyConditions), + summarize.WithConditions(gitRepositoryReadyCondition), summarize.WithReconcileResult(recResult), summarize.WithReconcileError(retErr), summarize.WithIgnoreNotFound(), @@ -164,7 +164,7 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques summarize.RecordContextualError, summarize.RecordReconcileReq, ), - summarize.WithResultBuilder(sreconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.GetInterval().Duration}), + summarize.WithResultBuilder(sreconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.GetRequeueAfter()}), } result, retErr = summarizeHelper.SummarizeAndPatch(ctx, obj, summarizeOpts...) @@ -188,7 +188,7 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // Reconcile actual object - reconcilers := []gitRepoReconcilerFunc{ + reconcilers := []gitRepositoryReconcileFunc{ r.reconcileStorage, r.reconcileSource, r.reconcileInclude, @@ -198,17 +198,15 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques return } -// reconcile steps iterates through the actual reconciliation tasks for objec, -// it returns early on the first step that returns ResultRequeue or produces an -// error. -func (r *GitRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.GitRepository, reconcilers []gitRepoReconcilerFunc) (sreconcile.Result, error) { +// reconcile iterates through the gitRepositoryReconcileFunc tasks for the +// object. It returns early on the first call that returns +// reconcile.ResultRequeue, or produces an error. +func (r *GitRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.GitRepository, reconcilers []gitRepositoryReconcileFunc) (sreconcile.Result, error) { + // Mark as reconciling if generation differs if obj.Generation != obj.Status.ObservedGeneration { conditions.MarkReconciling(obj, "NewGeneration", "reconciling new object generation (%d)", obj.Generation) } - var artifact sourcev1.Artifact - var includes artifactSet - // Create temp dir for Git clone tmpDir, err := util.TempDirForObj("", obj) if err != nil { @@ -217,11 +215,20 @@ func (r *GitRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.G Reason: sourcev1.StorageOperationFailedReason, } } - defer os.RemoveAll(tmpDir) + defer func() { + if err = os.RemoveAll(tmpDir); err != nil { + ctrl.LoggerFrom(ctx).Error(err, "failed to remove temporary working directory") + } + }() // Run the sub-reconcilers and build the result of reconciliation. - var res sreconcile.Result - var resErr error + var ( + artifact sourcev1.Artifact + includes artifactSet + + res sreconcile.Result + resErr error + ) for _, rec := range reconcilers { recResult, err := rec(ctx, obj, &artifact, &includes, tmpDir) // Exit immediately on ResultRequeue. @@ -241,13 +248,18 @@ func (r *GitRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.G return res, resErr } -// reconcileStorage ensures the current state of the storage matches the desired and previously observed state. +// reconcileStorage ensures the current state of the storage matches the +// desired and previously observed state. // -// All artifacts for the resource except for the current one are garbage collected from the storage. -// If the artifact in the Status object of the resource disappeared from storage, it is removed from the object. -// If the object does not have an artifact in its Status object, a v1beta1.ArtifactUnavailableCondition is set. -// If the hostname of any of the URLs on the object do not match the current storage server hostname, they are updated. -func (r *GitRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error) { +// All Artifacts for the object except for the current one in the Status are +// garbage collected from the Storage. +// If the Artifact in the Status of the object disappeared from the Storage, +// it is removed from the object. +// If the object does not have an Artifact in its Status, a Reconciling +// condition is added. +// The hostname of any URL in the Status of the object are updated, to ensure +// they match the Storage server hostname of current runtime. +func (r *GitRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sourcev1.GitRepository, _ *sourcev1.Artifact, _ *artifactSet, _ string) (sreconcile.Result, error) { // Garbage collect previous advertised artifact(s) from storage _ = r.garbageCollect(ctx, obj) @@ -271,19 +283,25 @@ func (r *GitRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sou return sreconcile.ResultSuccess, nil } -// reconcileSource ensures the upstream Git repository can be reached and checked out using the declared configuration, -// and observes its state. +// reconcileSource ensures the upstream Git repository and reference can be +// cloned and checked out using the specified configuration, and observes its +// state. // -// The repository is checked out to the given dir using the defined configuration, and in case of an error during the -// checkout process (including transient errors), it records v1beta1.FetchFailedCondition=True and returns early. -// On a successful checkout it removes v1beta1.FetchFailedCondition, and compares the current revision of HEAD to the -// artifact on the object, and records v1beta1.ArtifactOutdatedCondition if they differ. -// If instructed, the signature of the commit is verified if and recorded as v1beta1.SourceVerifiedCondition. If the -// signature can not be verified or the verification fails, the Condition=False and it returns early. -// If both the checkout and signature verification are successful, the given artifact pointer is set to a new artifact -// with the available metadata. -func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context, - obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, _ *artifactSet, dir string) (sreconcile.Result, error) { +// The repository is cloned to the given dir, using the specified configuration +// to check out the reference. In case of an error during this process +// (including transient errors), it records v1beta2.FetchFailedCondition=True +// and returns early. +// On a successful checkout, it removes v1beta2.FetchFailedCondition and +// compares the current revision of HEAD to the revision of the Artifact in the +// Status of the object. It records v1beta2.ArtifactOutdatedCondition=True when +// they differ. +// If specified, the signature of the Git commit is verified. If the signature +// can not be verified or the verification fails, it records +// v1beta2.SourceVerifiedCondition=False and returns early. When successful, +// it records v1beta2.SourceVerifiedCondition=True. +// When all the above is successful, the given Artifact pointer is set to a new +// Artifact composed out of the gathered Git metadata. +func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context, obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, _ *artifactSet, dir string) (sreconcile.Result, error) { // Configure authentication strategy to access the source var authOpts *git.AuthOptions var err error @@ -374,15 +392,17 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context, return sreconcile.ResultSuccess, nil } -// reconcileArtifact archives a new artifact to the storage, if the current observation on the object does not match the -// given data. +// reconcileArtifact archives a new Artifact to the Storage, if the current +// (Status) data on the object does not match the given. // -// The inspection of the given data to the object is differed, ensuring any stale observations as -// v1beta1.ArtifactUnavailableCondition and v1beta1.ArtifactOutdatedCondition are always deleted. -// If the given artifact and/or includes do not differ from the object's current, it returns early. -// Source ignore patterns are loaded, and the given directory is archived. -// On a successful archive, the artifact and includes in the status of the given object are set, and the symlink in the -// storage is updated to its path. +// The inspection of the given data to the object is differed, ensuring any +// stale observations like v1beta2.ArtifactOutdatedCondition are removed. +// If the given Artifact and/or artifactSet (includes) do not differ from the +// object's current, it returns early. +// Source ignore patterns are loaded, and the given directory is archived while +// taking these patterns into account. +// On a successful archive, the Artifact and Includes in the Status of the +// object are set, and the symlink in the Storage is updated to its path. func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error) { // Always restore the Ready condition in case it got removed due to a transient error defer func() { @@ -475,13 +495,19 @@ func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *so return sreconcile.ResultSuccess, nil } -// reconcileInclude reconciles the declared includes from the object by copying their artifact (sub)contents to the -// declared paths in the given directory. +// reconcileInclude reconciles the on the object specified +// v1beta2.GitRepositoryInclude list by copying their Artifact (sub)contents to +// the specified paths in the given directory. // -// If an include is unavailable, it marks the object with v1beta1.IncludeUnavailableCondition and returns early. -// If the copy operations are successful, it deletes the v1beta1.IncludeUnavailableCondition from the object. -// If the artifactSet differs from the current set, it marks the object with v1beta1.ArtifactOutdatedCondition. -func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context, obj *sourcev1.GitRepository, _ *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error) { +// When one of the includes is unavailable, it marks the object with +// v1beta2.IncludeUnavailableCondition=True and returns early. +// When the copy operations are successful, it removes the +// v1beta2.IncludeUnavailableCondition from the object. +// When the composed artifactSet differs from the current set in the Status of +// the object, it marks the object with v1beta2.ArtifactOutdatedCondition=True. +func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context, obj *sourcev1.GitRepository, + _ *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error) { + artifacts := make(artifactSet, len(obj.Spec.Include)) for i, incl := range obj.Spec.Include { // Do this first as it is much cheaper than copy operations @@ -542,25 +568,16 @@ func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context, obj *sou return sreconcile.ResultSuccess, nil } -// reconcileDelete handles the delete of an object. It first garbage collects all artifacts for the object from the -// artifact storage, if successful, the finalizer is removed from the object. -func (r *GitRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.GitRepository) (sreconcile.Result, error) { - // Garbage collect the resource's artifacts - if err := r.garbageCollect(ctx, obj); err != nil { - // Return the error so we retry the failed garbage collection - return sreconcile.ResultEmpty, err - } - - // Remove our finalizer from the list - controllerutil.RemoveFinalizer(obj, sourcev1.SourceFinalizer) - - // Stop reconciliation as the object is being deleted - return sreconcile.ResultEmpty, nil -} - -// verifyCommitSignature verifies the signature of the given commit if a verification mode is configured on the object. +// verifyCommitSignature verifies the signature of the given Git commit, if a +// verification mode is specified on the object. +// If the signature can not be verified or the verification fails, it records +// v1beta2.SourceVerifiedCondition=False and returns. +// When successful, it records v1beta2.SourceVerifiedCondition=True. +// If no verification mode is specified on the object, the +// v1beta2.SourceVerifiedCondition Condition is removed. func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj *sourcev1.GitRepository, commit git.Commit) (sreconcile.Result, error) { - // Check if there is a commit verification is configured and remove any old observations if there is none + // Check if there is a commit verification is configured and remove any old + // observations if there is none if obj.Spec.Verification == nil || obj.Spec.Verification.Mode == "" { conditions.Delete(obj, sourcev1.SourceVerifiedCondition) return sreconcile.ResultSuccess, nil @@ -603,9 +620,28 @@ func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj return sreconcile.ResultSuccess, nil } -// garbageCollect performs a garbage collection for the given v1beta1.GitRepository. It removes all but the current -// artifact except for when the deletion timestamp is set, which will result in the removal of all artifacts for the -// resource. +// reconcileDelete handles the deletion of the object. +// It first garbage collects all Artifacts for the object from the Storage. +// Removing the finalizer from the object if successful. +func (r *GitRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.GitRepository) (sreconcile.Result, error) { + // Garbage collect the resource's artifacts + if err := r.garbageCollect(ctx, obj); err != nil { + // Return the error so we retry the failed garbage collection + return sreconcile.ResultEmpty, err + } + + // Remove our finalizer from the list + controllerutil.RemoveFinalizer(obj, sourcev1.SourceFinalizer) + + // Stop reconciliation as the object is being deleted + return sreconcile.ResultEmpty, nil +} + +// garbageCollect performs a garbage collection for the given object. +// +// It removes all but the current Artifact from the Storage, unless the +// deletion timestamp on the object is set. Which will result in the +// removal of all Artifacts for the objects. func (r *GitRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourcev1.GitRepository) error { if !obj.DeletionTimestamp.IsZero() { if deleted, err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil { @@ -633,9 +669,11 @@ func (r *GitRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourc return nil } -// eventLog records event and logs at the same time. This log is different from -// the debug log in the event recorder in the sense that this is a simple log, -// the event recorder debug log contains complete details about the event. +// eventLogf records event and logs at the same time. +// +// This log is different from the debug log in the EventRecorder, in the sense +// that this is a simple log. While the debug log contains complete details +// about the event. func (r *GitRepositoryReconciler) eventLogf(ctx context.Context, obj runtime.Object, eventType string, reason string, messageFmt string, args ...interface{}) { msg := fmt.Sprintf(messageFmt, args...) // Log and emit event. diff --git a/controllers/gitrepository_controller_test.go b/controllers/gitrepository_controller_test.go index 6549647e..db0a0fc6 100644 --- a/controllers/gitrepository_controller_test.go +++ b/controllers/gitrepository_controller_test.go @@ -191,7 +191,7 @@ func TestGitRepositoryReconciler_Reconcile(t *testing.T) { }, timeout).Should(BeTrue()) // Check if the object status is valid. - condns := &status.Conditions{NegativePolarity: gitRepoReadyConditions.NegativePolarity} + condns := &status.Conditions{NegativePolarity: gitRepositoryReadyCondition.NegativePolarity} checker := status.NewChecker(testEnv.Client, testEnv.GetScheme(), condns) checker.CheckErr(ctx, obj) diff --git a/docs/api/source.md b/docs/api/source.md index 6f46cff8..43b51e56 100644 --- a/docs/api/source.md +++ b/docs/api/source.md @@ -240,7 +240,7 @@ BucketStatus
GitRepository is the Schema for the gitrepositories API
+GitRepository is the Schema for the gitrepositories API.
- The repository URL, can be a HTTP/S or SSH address. +URL specifies the Git repository URL, can be an HTTP/S or SSH address. |
(Optional)
- The secret name containing the Git credentials. -For HTTPS repositories the secret must contain username and password fields. -For SSH repositories the secret must contain ‘identity’, ‘identity.pub’ and ‘known_hosts’ fields. +SecretRef specifies the Secret containing authentication credentials for +the GitRepository. +For HTTPS repositories the Secret must contain ‘username’ and ‘password’ +fields. +For SSH repositories the Secret must contain ‘identity’, ‘identity.pub’ +and ‘known_hosts’ fields. |
- The interval at which to check for repository updates. +Interval at which to check the GitRepository for updates. |
(Optional)
- The timeout for remote Git operations like cloning, defaults to 60s. +Timeout for Git operations like cloning, defaults to 60s. |
(Optional)
- The Git reference to checkout and monitor for changes, defaults to -master branch. +Reference specifies the Git reference to resolve and monitor for +changes, defaults to the ‘master’ branch. |
(Optional)
- Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to. +Verification specifies the configuration to verify the Git commit +signature(s). |
(Optional)
- Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore). -If not provided, a default will be used, consult the documentation for your version to find out what those are. +Ignore overrides the set of excluded patterns in the .sourceignore format +(which is the same as .gitignore). If not provided, a default will be used, +consult the documentation for your version to find out what those are. |
(Optional)
- Suspend tells the controller to suspend the reconciliation of this source. -This flag tells the controller to suspend the reconciliation of this source. +Suspend tells the controller to suspend the reconciliation of this +GitRepository. |
(Optional)
- Determines which git client library to use. -Defaults to go-git, valid values are (‘go-git’, ‘libgit2’). +GitImplementation specifies which Git client library implementation to +use. Defaults to ‘go-git’, valid values are (‘go-git’, ‘libgit2’). |
(Optional)
- When enabled, after the clone is created, initializes all submodules within, using their default settings. + RecurseSubmodules enables the initialization of all submodules within +the GitRepository as cloned from the URL, using their default settings. This option is available only when using the ‘go-git’ GitImplementation. |
- Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for -this resource. +Include specifies a list of GitRepository resources which Artifacts +should be included in the Artifact produced for this GitRepository. |
(Optional)
- AccessFrom defines an Access Control List for allowing cross-namespace references to this object. +AccessFrom specifies an Access Control List for allowing cross-namespace +references to this object. |
GitRepositoryInclude defines a source with a from and to path.
+GitRepositoryInclude specifies a reference to a GitRepository which Artifact +(sub-)contents must be included, and where they should be placed.
- Reference to a GitRepository to include. +GitRepositoryRef specifies the GitRepository which Artifact contents +must be included. |
|
(Optional)
- The path to copy contents from, defaults to the root directory. +FromPath specifies the path to copy contents from, defaults to the root +of the Artifact. |
|
(Optional)
- The path to copy contents to, defaults to the name of the source ref. +ToPath specifies the path to copy contents to, defaults to the name of +the GitRepositoryRef. |
(Optional)
- The Git branch to checkout, defaults to master. +Branch to checkout, defaults to ‘master’ if no other field is defined. +When GitRepositorySpec.GitImplementation is set to ‘go-git’, a shallow +clone of the specified branch is performed. |
|
(Optional)
- The Git tag to checkout, takes precedence over Branch. +Tag to checkout, takes precedence over Branch. |
|
(Optional)
- The Git tag semver expression, takes precedence over Tag. +SemVer tag expression to checkout, takes precedence over Tag. |
|
(Optional)
- The Git commit SHA to checkout, if specified Tag filters will be ignored. +Commit SHA to checkout, takes precedence over all reference fields. +When GitRepositorySpec.GitImplementation is set to ‘go-git’, this can be +combined with Branch to shallow clone the branch, in which the commit is +expected to exist. |
- The repository URL, can be a HTTP/S or SSH address. +URL specifies the Git repository URL, can be an HTTP/S or SSH address. |
|
(Optional)
- The secret name containing the Git credentials. -For HTTPS repositories the secret must contain username and password fields. -For SSH repositories the secret must contain ‘identity’, ‘identity.pub’ and ‘known_hosts’ fields. +SecretRef specifies the Secret containing authentication credentials for +the GitRepository. +For HTTPS repositories the Secret must contain ‘username’ and ‘password’ +fields. +For SSH repositories the Secret must contain ‘identity’, ‘identity.pub’ +and ‘known_hosts’ fields. |
|
- The interval at which to check for repository updates. +Interval at which to check the GitRepository for updates. |
|
(Optional)
- The timeout for remote Git operations like cloning, defaults to 60s. +Timeout for Git operations like cloning, defaults to 60s. |
|
(Optional)
- The Git reference to checkout and monitor for changes, defaults to -master branch. +Reference specifies the Git reference to resolve and monitor for +changes, defaults to the ‘master’ branch. |
|
(Optional)
- Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to. +Verification specifies the configuration to verify the Git commit +signature(s). |
|
(Optional)
- Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore). -If not provided, a default will be used, consult the documentation for your version to find out what those are. +Ignore overrides the set of excluded patterns in the .sourceignore format +(which is the same as .gitignore). If not provided, a default will be used, +consult the documentation for your version to find out what those are. |
|
(Optional)
- Suspend tells the controller to suspend the reconciliation of this source. -This flag tells the controller to suspend the reconciliation of this source. +Suspend tells the controller to suspend the reconciliation of this +GitRepository. |
|
(Optional)
- Determines which git client library to use. -Defaults to go-git, valid values are (‘go-git’, ‘libgit2’). +GitImplementation specifies which Git client library implementation to +use. Defaults to ‘go-git’, valid values are (‘go-git’, ‘libgit2’). |
|
(Optional)
- When enabled, after the clone is created, initializes all submodules within, using their default settings. + RecurseSubmodules enables the initialization of all submodules within +the GitRepository as cloned from the URL, using their default settings. This option is available only when using the ‘go-git’ GitImplementation. |
- Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for -this resource. +Include specifies a list of GitRepository resources which Artifacts +should be included in the Artifact produced for this GitRepository. |
(Optional)
- AccessFrom defines an Access Control List for allowing cross-namespace references to this object. +AccessFrom specifies an Access Control List for allowing cross-namespace +references to this object. |
(Optional)
- ObservedGeneration is the last observed generation. +ObservedGeneration is the last observed generation of the GitRepository +object. |
|
(Optional)
- URL is the fetch link for the artifact output of the last repository sync. +URL is the dynamic fetch link for the latest Artifact. +It is provided on a “best effort” basis, and using the precise +GitRepositoryStatus.Artifact data is recommended. |
|
(Optional)
- Artifact represents the output of the last successful repository sync. +Artifact represents the last successful GitRepository reconciliation. |
|
(Optional)
- IncludedArtifacts represents the included artifacts from the last successful repository sync. +IncludedArtifacts contains a list of the last successfully included +Artifacts as instructed by GitRepositorySpec.Include. |
|
- Mode describes what Git object should be verified, currently (‘head’). +Mode specifies what Git object should be verified, currently (‘head’). |
- SecretRef containing the public keys of all trusted Git authors. +SecretRef specifies the Secret containing the public keys of trusted Git +authors. |