From 861343d18efed4fe78eb8fe412ec280764f1aa3f Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Tue, 28 Mar 2023 11:53:30 +0200 Subject: [PATCH] Put back deprecated types and hint to v1 Signed-off-by: Hidde Beydals --- api/v1beta2/artifact_types.go | 159 +++++++++++++++++++++++++++ api/v1beta2/artifact_types_test.go | 78 +++++++++++++ api/v1beta2/condition_types.go | 107 ++++++++++++++++++ api/v1beta2/source.go | 48 ++++++++ api/v1beta2/zz_generated.deepcopy.go | 30 ++++- docs/api/v1beta2/source.md | 129 ++++++++++++++++++++++ 6 files changed, 550 insertions(+), 1 deletion(-) create mode 100644 api/v1beta2/artifact_types.go create mode 100644 api/v1beta2/artifact_types_test.go create mode 100644 api/v1beta2/condition_types.go create mode 100644 api/v1beta2/source.go diff --git a/api/v1beta2/artifact_types.go b/api/v1beta2/artifact_types.go new file mode 100644 index 00000000..cc88d2a0 --- /dev/null +++ b/api/v1beta2/artifact_types.go @@ -0,0 +1,159 @@ +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import ( + "path" + "regexp" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Artifact represents the output of a Source reconciliation. +// +// Deprecated: use Artifact from api/v1 instead. This type will be removed in +// a future release. +type Artifact struct { + // Path is the relative file path of the Artifact. It can be used to locate + // the file in the root of the Artifact storage on the local file system of + // the controller managing the Source. + // +required + Path string `json:"path"` + + // URL is the HTTP address of the Artifact as exposed by the controller + // managing the Source. It can be used to retrieve the Artifact for + // consumption, e.g. by another controller applying the Artifact contents. + // +required + URL string `json:"url"` + + // Revision is a human-readable identifier traceable in the origin source + // system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + // +optional + Revision string `json:"revision"` + + // Checksum is the SHA256 checksum of the Artifact file. + // Deprecated: use Artifact.Digest instead. + // +optional + Checksum string `json:"checksum,omitempty"` + + // Digest is the digest of the file in the form of ':'. + // +optional + // +kubebuilder:validation:Pattern="^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$" + Digest string `json:"digest,omitempty"` + + // LastUpdateTime is the timestamp corresponding to the last update of the + // Artifact. + // +required + LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` + + // Size is the number of bytes in the file. + // +optional + Size *int64 `json:"size,omitempty"` + + // Metadata holds upstream information such as OCI annotations. + // +optional + Metadata map[string]string `json:"metadata,omitempty"` +} + +// HasRevision returns if the given revision matches the current Revision of +// the Artifact. +func (in *Artifact) HasRevision(revision string) bool { + if in == nil { + return false + } + return TransformLegacyRevision(in.Revision) == TransformLegacyRevision(revision) +} + +// HasChecksum returns if the given checksum matches the current Checksum of +// the Artifact. +func (in *Artifact) HasChecksum(checksum string) bool { + if in == nil { + return false + } + return in.Checksum == checksum +} + +// ArtifactDir returns the artifact dir path in the form of +// '//'. +func ArtifactDir(kind, namespace, name string) string { + kind = strings.ToLower(kind) + return path.Join(kind, namespace, name) +} + +// ArtifactPath returns the artifact path in the form of +// '//name>/'. +func ArtifactPath(kind, namespace, name, filename string) string { + return path.Join(ArtifactDir(kind, namespace, name), filename) +} + +// TransformLegacyRevision transforms a "legacy" revision string into a "new" +// revision string. It accepts the following formats: +// +// - main/5394cb7f48332b2de7c17dd8b8384bbc84b7e738 +// - feature/branch/5394cb7f48332b2de7c17dd8b8384bbc84b7e738 +// - HEAD/5394cb7f48332b2de7c17dd8b8384bbc84b7e738 +// - tag/55609ff9d959589ed917ce32e6bc0f0a36809565f308602c15c3668965979edc +// - d52bde83c5b2bd0fa7910264e0afc3ac9cfe9b6636ca29c05c09742f01d5a4bd +// +// Which are transformed into the following formats respectively: +// +// - main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738 +// - feature/branch@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738 +// - sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738 +// - tag@sha256:55609ff9d959589ed917ce32e6bc0f0a36809565f308602c15c3668965979edc +// - sha256:d52bde83c5b2bd0fa7910264e0afc3ac9cfe9b6636ca29c05c09742f01d5a4bd +// +// Deprecated, this function exists for backwards compatibility with existing +// resources, and to provide a transition period. Will be removed in a future +// release. +func TransformLegacyRevision(rev string) string { + if rev != "" && strings.LastIndex(rev, ":") == -1 { + if i := strings.LastIndex(rev, "/"); i >= 0 { + sha := rev[i+1:] + if algo := determineSHAType(sha); algo != "" { + if name := rev[:i]; name != "HEAD" { + return name + "@" + algo + ":" + sha + } + return algo + ":" + sha + } + } + if algo := determineSHAType(rev); algo != "" { + return algo + ":" + rev + } + } + return rev +} + +// isAlphaNumHex returns true if the given string only contains 0-9 and a-f +// characters. +var isAlphaNumHex = regexp.MustCompile(`^[0-9a-f]+$`).MatchString + +// determineSHAType returns the SHA algorithm used to compute the provided hex. +// The determination is heuristic and based on the length of the hex string. If +// the size is not recognized, an empty string is returned. +func determineSHAType(hex string) string { + if isAlphaNumHex(hex) { + switch len(hex) { + case 40: + return "sha1" + case 64: + return "sha256" + } + } + return "" +} diff --git a/api/v1beta2/artifact_types_test.go b/api/v1beta2/artifact_types_test.go new file mode 100644 index 00000000..ccf578de --- /dev/null +++ b/api/v1beta2/artifact_types_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2023 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import "testing" + +func TestTransformLegacyRevision(t *testing.T) { + tests := []struct { + rev string + want string + }{ + { + rev: "HEAD/5394cb7f48332b2de7c17dd8b8384bbc84b7e738", + want: "sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738", + }, + { + rev: "main/5394cb7f48332b2de7c17dd8b8384bbc84b7e738", + want: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738", + }, + { + rev: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738", + want: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738", + }, + { + rev: "feature/branch/5394cb7f48332b2de7c17dd8b8384bbc84b7e738", + want: "feature/branch@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738", + }, + { + rev: "feature/branch@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738", + want: "feature/branch@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738", + }, + { + rev: "5ac85ca617f3774baff4ae0a420b810b2546dbc9af9f346b1d55c5ed9873c55c", + want: "sha256:5ac85ca617f3774baff4ae0a420b810b2546dbc9af9f346b1d55c5ed9873c55c", + }, + { + rev: "v1.0.0", + want: "v1.0.0", + }, + { + rev: "v1.0.0-rc1", + want: "v1.0.0-rc1", + }, + { + rev: "v1.0.0-rc1+metadata", + want: "v1.0.0-rc1+metadata", + }, + { + rev: "arbitrary/revision", + want: "arbitrary/revision", + }, + { + rev: "5394cb7f48332b2de7c17dd8b8384bbc84b7xxxx", + want: "5394cb7f48332b2de7c17dd8b8384bbc84b7xxxx", + }, + } + for _, tt := range tests { + t.Run(tt.rev, func(t *testing.T) { + if got := TransformLegacyRevision(tt.rev); got != tt.want { + t.Errorf("TransformLegacyRevision() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/api/v1beta2/condition_types.go b/api/v1beta2/condition_types.go new file mode 100644 index 00000000..2b93a179 --- /dev/null +++ b/api/v1beta2/condition_types.go @@ -0,0 +1,107 @@ +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +const SourceFinalizer = "finalizers.fluxcd.io" + +const ( + // ArtifactInStorageCondition indicates the availability of the Artifact in + // the storage. + // If True, the Artifact is stored successfully. + // This Condition is only present on the resource if the Artifact is + // successfully stored. + ArtifactInStorageCondition string = "ArtifactInStorage" + + // ArtifactOutdatedCondition indicates the current Artifact of the Source + // is outdated. + // This is a "negative polarity" or "abnormal-true" type, and is only + // present on the resource if it is True. + ArtifactOutdatedCondition string = "ArtifactOutdated" + + // SourceVerifiedCondition indicates the integrity verification of the + // Source. + // If True, the integrity check succeeded. If False, it failed. + // This Condition is only present on the resource if the integrity check + // is enabled. + SourceVerifiedCondition string = "SourceVerified" + + // FetchFailedCondition indicates a transient or persistent fetch failure + // of an upstream Source. + // If True, observations on the upstream Source revision may be impossible, + // and the Artifact available for the Source may be outdated. + // This is a "negative polarity" or "abnormal-true" type, and is only + // present on the resource if it is True. + FetchFailedCondition string = "FetchFailed" + + // BuildFailedCondition indicates a transient or persistent build failure + // of a Source's Artifact. + // If True, the Source can be in an ArtifactOutdatedCondition. + // This is a "negative polarity" or "abnormal-true" type, and is only + // present on the resource if it is True. + BuildFailedCondition string = "BuildFailed" + + // StorageOperationFailedCondition indicates a transient or persistent + // failure related to storage. If True, the reconciliation failed while + // performing some filesystem operation. + // This is a "negative polarity" or "abnormal-true" type, and is only + // present on the resource if it is True. + StorageOperationFailedCondition string = "StorageOperationFailed" +) + +// Reasons are provided as utility, and not part of the declarative API. +const ( + // URLInvalidReason signals that a given Source has an invalid URL. + URLInvalidReason string = "URLInvalid" + + // AuthenticationFailedReason signals that a Secret does not have the + // required fields, or the provided credentials do not match. + AuthenticationFailedReason string = "AuthenticationFailed" + + // VerificationError signals that the Source's verification + // check failed. + VerificationError string = "VerificationError" + + // DirCreationFailedReason signals a failure caused by a directory creation + // operation. + DirCreationFailedReason string = "DirectoryCreationFailed" + + // StatOperationFailedReason signals a failure caused by a stat operation on + // a path. + StatOperationFailedReason string = "StatOperationFailed" + + // ReadOperationFailedReason signals a failure caused by a read operation. + ReadOperationFailedReason string = "ReadOperationFailed" + + // AcquireLockFailedReason signals a failure in acquiring lock. + AcquireLockFailedReason string = "AcquireLockFailed" + + // InvalidPathReason signals a failure caused by an invalid path. + InvalidPathReason string = "InvalidPath" + + // ArchiveOperationFailedReason signals a failure in archive operation. + ArchiveOperationFailedReason string = "ArchiveOperationFailed" + + // SymlinkUpdateFailedReason signals a failure in updating a symlink. + SymlinkUpdateFailedReason string = "SymlinkUpdateFailed" + + // ArtifactUpToDateReason signals that an existing Artifact is up-to-date + // with the Source. + ArtifactUpToDateReason string = "ArtifactUpToDate" + + // CacheOperationFailedReason signals a failure in cache operation. + CacheOperationFailedReason string = "CacheOperationFailed" +) diff --git a/api/v1beta2/source.go b/api/v1beta2/source.go new file mode 100644 index 00000000..4111c099 --- /dev/null +++ b/api/v1beta2/source.go @@ -0,0 +1,48 @@ +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import ( + "time" + + "k8s.io/apimachinery/pkg/runtime" +) + +const ( + // SourceIndexKey is the key used for indexing objects based on their + // referenced Source. + SourceIndexKey string = ".metadata.source" +) + +// Source interface must be supported by all API types. +// Source is the interface that provides generic access to the Artifact and +// interval. It must be supported by all kinds of the source.toolkit.fluxcd.io +// API group. +// +// Deprecated: use the Source interface from api/v1 instead. This type will be +// removed in a future release. +// +// +k8s:deepcopy-gen=false +type Source interface { + runtime.Object + // GetRequeueAfter returns the duration after which the source must be + // reconciled again. + GetRequeueAfter() time.Duration + // GetArtifact returns the latest artifact from the source if present in + // the status sub-resource. + GetArtifact() *Artifact +} diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index cc288c8c..7393ecbb 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -26,9 +26,37 @@ import ( "github.com/fluxcd/pkg/apis/meta" apiv1 "github.com/fluxcd/source-controller/api/v1" "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Artifact) DeepCopyInto(out *Artifact) { + *out = *in + in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) + if in.Size != nil { + in, out := &in.Size, &out.Size + *out = new(int64) + **out = **in + } + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Artifact. +func (in *Artifact) DeepCopy() *Artifact { + if in == nil { + return nil + } + out := new(Artifact) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Bucket) DeepCopyInto(out *Bucket) { *out = *in diff --git a/docs/api/v1beta2/source.md b/docs/api/v1beta2/source.md index 25168008..cadc8649 100644 --- a/docs/api/v1beta2/source.md +++ b/docs/api/v1beta2/source.md @@ -1183,6 +1183,127 @@ OCIRepositoryStatus +

Artifact +

+

Artifact represents the output of a Source reconciliation.

+

Deprecated: use Artifact from api/v1 instead. This type will be removed in +a future release.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+path
+ +string + +
+

Path is the relative file path of the Artifact. It can be used to locate +the file in the root of the Artifact storage on the local file system of +the controller managing the Source.

+
+url
+ +string + +
+

URL is the HTTP address of the Artifact as exposed by the controller +managing the Source. It can be used to retrieve the Artifact for +consumption, e.g. by another controller applying the Artifact contents.

+
+revision
+ +string + +
+(Optional) +

Revision is a human-readable identifier traceable in the origin source +system. It can be a Git commit SHA, Git tag, a Helm chart version, etc.

+
+checksum
+ +string + +
+(Optional) +

Checksum is the SHA256 checksum of the Artifact file. +Deprecated: use Artifact.Digest instead.

+
+digest
+ +string + +
+(Optional) +

Digest is the digest of the file in the form of ‘:’.

+
+lastUpdateTime
+ + +Kubernetes meta/v1.Time + + +
+

LastUpdateTime is the timestamp corresponding to the last update of the +Artifact.

+
+size
+ +int64 + +
+(Optional) +

Size is the number of bytes in the file.

+
+metadata
+ +map[string]string + +
+(Optional) +

Metadata holds upstream information such as OCI annotations.

+
+
+

BucketSpec

@@ -3090,6 +3211,14 @@ trusted public keys.

+

Source +

+

Source interface must be supported by all API types. +Source is the interface that provides generic access to the Artifact and +interval. It must be supported by all kinds of the source.toolkit.fluxcd.io +API group.

+

Deprecated: use the Source interface from api/v1 instead. This type will be +removed in a future release.

This page was automatically generated with gen-crd-api-reference-docs