Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
192035c
feat: add git commit author configuration and improve logging
matteogastaldello Nov 21, 2025
c399de7
feat: implement LocalResource API and CRD for managing Git repositories
matteogastaldello Nov 21, 2025
01c1544
feat(hash): add ObjectHash implementation for cumulative hashing
matteogastaldello Nov 21, 2025
c90d2cb
feat: enhance LocalResource handling with dynamic interface and conte…
matteogastaldello Nov 21, 2025
e12f993
feat: update Go version in Dockerfile to 1.25.o-bookworm
matteogastaldello Nov 24, 2025
2baed14
fix: increase wait time for Gitea readiness in integration test
matteogastaldello Nov 24, 2025
a1b2d97
fix: correct Go version in Dockerfile from 1.25.o-bookworm to 1.25.0-…
matteogastaldello Nov 24, 2025
7d21306
test: update Gitea port from 443 to 3443 in integration tests
matteogastaldello Nov 24, 2025
1aed017
test: implement waitForGitea function to check Gitea readiness before…
matteogastaldello Nov 24, 2025
becde8b
test: update Gitea port from 443 to 8443 in test configurations
matteogastaldello Nov 24, 2025
7d40854
test: update Gitea URLs from localhost to 127.0.0.1 in test configura…
matteogastaldello Nov 28, 2025
6313e22
test: set NetworkMode to host in TestMain for local resource tests
matteogastaldello Dec 2, 2025
59b77c1
test: update HostIP in port bindings to use localhost (127.0.0.1)
matteogastaldello Dec 2, 2025
bc53997
test: refactor Gitea credentials to use variables for better maintain…
matteogastaldello Dec 2, 2025
c6e6fd3
test: enhance TestMain with container network settings and logging fo…
matteogastaldello Dec 2, 2025
85eacf9
test: update TestMain to set ownership for /data and improve comments…
matteogastaldello Dec 2, 2025
b7d15ce
test: update TestMain to ensure ownership of /data is set after certi…
matteogastaldello Dec 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build environment
# -----------------
FROM golang:1.24-bookworm AS builder
FROM golang:1.25.0-bookworm AS builder
LABEL stage=builder

ARG DEBIAN_FRONTEND=noninteractive
Expand Down
2 changes: 2 additions & 0 deletions apis/git.go → apis/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package apis
import (
"k8s.io/apimachinery/pkg/runtime"

localresourcev1alpha1 "github.com/krateoplatformops/git-provider/apis/localresource/v1alpha1"
repov1alpha1 "github.com/krateoplatformops/git-provider/apis/repo/v1alpha1"
)

func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes,
repov1alpha1.SchemeBuilder.AddToScheme,
localresourcev1alpha1.SchemeBuilder.AddToScheme,
)
}

Expand Down
37 changes: 37 additions & 0 deletions apis/localresource/v1alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Package v1alpha1 contains API Schema definitions for the git v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=git.krateo.io
// +versionName=v1alpha1
package v1alpha1

import (
"reflect"

"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)

// Package type metadata.
const (
Group = "git.krateo.io"
Version = "v1alpha1"
)

var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
)

var (
LocalResourceKind = reflect.TypeOf(LocalResource{}).Name()
LocalResourceGroupKind = schema.GroupKind{Group: Group, Kind: LocalResourceKind}.String()
LocalResourceKindAPIVersion = LocalResourceKind + "." + SchemeGroupVersion.String()
LocalResourceGroupVersionKind = SchemeGroupVersion.WithKind(LocalResourceKind)
)

func init() {
SchemeBuilder.Register(&LocalResource{}, &LocalResourceList{})
}
213 changes: 213 additions & 0 deletions apis/localresource/v1alpha1/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package v1alpha1

import (
prv1 "github.com/krateoplatformops/provider-runtime/apis/common/v1"
"github.com/krateoplatformops/provider-runtime/pkg/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

type Credentials struct {
// AuthMethod: Possible values are: `basic`, `bearer`, `cookiefile`. `basic` requires `secretRef` and `usernameRef`; `basic` requires only `secretRef`; `cookiefile` requires only `secretRef`
// In case of 'cookiefile' the secretRef must contain a file with the cookie.
// +kubebuilder:validation:Enum=basic;bearer;cookiefile
// +kubebuilder:default:=basic
// +optional
AuthMethod string `json:"authMethod,omitempty"`

// SecretRef: reference to a secret that contains token required to git server authentication or cookie file in case of 'cookiefile' authMethod.
SecretRef *prv1.SecretKeySelector `json:"secretRef"`

// UsernameRef: holds username required to git server authentication. - If 'authMethod' is 'bearer' or 'cookiefile' the field is ignored.
// +optional
UsernameRef *prv1.SecretKeySelector `json:"usernameRef"`
}
type LocalResourceOpts struct {
// Url: url of the remote repository
Url string `json:"url"`

// Path: if in spec.fromLocalResource, Represents the folder to clone from. If not set the entire repository is cloned. If in spec.toLocalResource, represents the folder to use as destination.
// +kubebuilder:default:="/"
// +optional
Path string `json:"path,omitempty"`

// Branch: if in spec.fromLocalResource, the branch to copy from. If in spec.toLocalResource, represents the branch to populate; If the branch does not exist on remote is created by the provider.
// +required
Branch string `json:"branch"`

// Credentials: Credentials required for git server authentication.
// +required
Credentials Credentials `json:"credentials,omitempty"`
/*
CloneFromBranch: used the parent of the new branch.
- If the branch exists, the parameter is ignored.
- If the parameter is not set, the branch is created empty and has no parents (no history) - `git switch --orphan branch-name`
*/
// +optional
CloneFromBranch string `json:"cloneFromBranch,omitempty"`
}

type ResourceRef struct {
// Name: Name of the resource to reference
// +required
Name string `json:"name"`

// Namespace: Namespace of the resource to reference. If not set, the resource is expected to be cluster-scoped.
// +optional
Namespace string `json:"namespace,omitempty"`

// ApiVersion: API version of the resource to reference
// +required
ApiVersion string `json:"apiVersion"`

// Resource: Kind of the resource to reference
// +required
Resource string `json:"resource"`
}

type Placeholder struct {
// Name: Name of the placeholder to replace in the format {{ .name }}
// +required
Name string `json:"name"`

// Value: Value to replace the placeholder with
// +required
Value string `json:"value"`
}

// +kubebuilder:validation:XValidation:rule="!has(self.fromString) || has(self.fileName)",message="fileName is required when fromString is set"
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.fromRef) || self.fromRef == oldSelf.fromRef",message="fromRef is immutable once set"
// +kubebuilder:validation:ExactlyOneOf=fromYaml;fromRef;fromString
type FromResource struct {
// FileName: represent the file name to use. If not set the filename is defined as "gvk"_"metadata.name"_"metadata.namespace".yaml
// +kubebuilder:validation:Pattern=`^[\w\-. {}]+$`
// +optional
FileName string `json:"fileName,omitempty"`

// FromYaml: Valid K8s Manifest to copy from. It is considered valid if it contains Kind, APIVersion and (Name or GenerateName) fields.
// +kubebuilder:validation:EmbeddedResource
// +optional
FromYaml *runtime.RawExtension `json:"fromYaml,omitempty"`

// FromRef: Reference to a resource in the same cluster to copy from
// +optional
FromRef *ResourceRef `json:"fromRef,omitempty"`

// FromString: This can cointain a string representation of a K8s manifest to copy from or any other text content. This is not validated but it preserves the complete ordering of fields as in the original string.
// +optional
FromString *string `json:"fromString,omitempty"`
}

// A LocalResourceSpec defines the desired state of a LocalResource.
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.toRepo) || self.toRepo == oldSelf.toRepo",message="toRepo is immutable once set"
// +kubebuilder:validation:XValidation:rule="self.syncEnabled || self.override || !has(oldSelf.placeholdersToOverride) || self.placeholdersToOverride == oldSelf.placeholdersToOverride",message="placeholdersToOverride is immutable when syncEnabled is false"
// +kubebuilder:validation:XValidation:rule="self.syncEnabled || self.override || !has(oldSelf.fromResource.fromYaml) || self.fromResource.fromYaml == oldSelf.fromResource.fromYaml",message="fromResource.fromYaml is immutable when syncEnabled is false"
// +kubebuilder:validation:XValidation:rule="self.syncEnabled || self.override || !has(oldSelf.fromResource.fromString) || self.fromResource.fromString == oldSelf.fromResource.fromString",message="fromResource.fromString is immutable when syncEnabled is false"
// +kubebuilder:validation:XValidation:rule="!self.toRepo.url.contains('dev.azure.com') || self.unsupportedCapabilities == true",message="spec.unsupportedCapabilities must be true if toRepo.url contains 'dev.azure.com', as the go-git library does not support required capabilities for Azure DevOps. You can read more about that in the description of the 'unsupportedCapabilities' field."
type LocalResourceSpec struct {
// PlaceholdersToOverride: List of placeholders to override in the format {{ .placeholder }}.
// +optional
PlaceholdersToOverride []Placeholder `json:"placeholdersToOverride,omitempty"`

// FromResource: Resource to copy from
// +required
FromResource FromResource `json:"fromResource"`

// ToRepo: Repository destination to copy to
// +required
ToRepo LocalResourceOpts `json:"toRepo"`

// Insecure: Insecure is useful with hand made SSL certs (default: false)
// +optional
Insecure bool `json:"insecure,omitempty"`

// UnsupportedCapabilities: If `true` [capabilities not supported by any client implementation](https://github.com/go-git/go-git/blob/4fd9979d5c2940e72bdd6946fec21e02d959f0f6/plumbing/transport/common.go#L310) will not be used by the provider
// For azuredevops urls for example, the value must be true. Azure DevOps requires multi_ack and multi_ack_detailed capabilities, which go-git doesn't
// implement. But: it's possible to do a full clone by saying it's _not_ _un_supported, in which
// case the library happily functions so long as it doesn't _actually_ get a multi_ack packet. See
// https://github.com/go-git/go-git/blob/v5.5.1/_examples/azure_devops/main.go.
// +optional
// +kubebuilder:default:=false
UnsupportedCapabilities bool `json:"unsupportedCapabilities,omitempty"`

// SyncEnabled: If `true`, the provider performs updates on the repository specified in `toLocalResource` when there are changes in `fromResource` or `fromResourceRef` field.
// +kubebuilder:default:=false
// +optional
SyncEnabled bool `json:"syncEnabled,omitempty"`

// CreateCommitMessage: Commit message to use when creating new files in the destination repository. A description will be appeded on the commit message indicating some information about the CR that have triggered the commit.
// +optional
// +kubebuilder:default:="chore: add files to remote repository"
// +kubebuilder:validation:XValidation:rule="!self.contains('Managed by git-provider LocalResource:')",message="Il campo updateCommitMessage non può contenere la sottostringa 'Managed by git-provider LocalResource:'"
CreateCommitMessage string `json:"createCommitMessage,omitempty"`

// UpdateCommitMessage: Commit message to use when updating existing files in the destination repository. A description will be appeded on the commit message indicating some information about the CR that have triggered the commit.
// +optional
// +kubebuilder:default:="chore: update files in remote repository"
// +kubebuilder:validation:XValidation:rule="!self.contains('Managed by git-provider LocalResource:')",message="Il campo updateCommitMessage non può contenere la sottostringa 'Managed by git-provider LocalResource:'"
UpdateCommitMessage string `json:"updateCommitMessage,omitempty"`

// Override: If `true`, the provider will override the existing files in the destination repository with the files from the source repository.
// If `false`, the provider will only add new files and update existing files in the destination repository.
// If not set, the provider will use the default behavior of adding new files.
// Avoid using this option with originPath from / to /, as it will override also service folders like .git, .github, .gitignore, etc.
// +kubebuilder:default:=false
// +optional
Override bool `json:"override,omitempty"`
}

// A LocalResourceStatus represents the observed state of a LocalResource.
type LocalResourceStatus struct {
prv1.ConditionedStatus `json:",inline"`
// TargetCommitId: last commit identifier of the target LocalResource
TargetCommitId string `json:"targetCommitId,omitempty"`

// TargetBranch: branch where commit was done
TargetBranch string `json:"targetBranch,omitempty"`
}

// +kubebuilder:object:root=true

// A LocalResource is a managed resource that represents a Krateo Git repository
// +kubebuilder:printcolumn:name="TARGET_COMMIT_ID",type="string",JSONPath=".status.targetCommitId"
// +kubebuilder:printcolumn:name="TARGET_BRANCH",type="string",JSONPath=".status.targetBranch"
// +kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status"
// +kubebuilder:printcolumn:name="SYNCED",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status"
// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Namespaced,categories={git,krateo}
type LocalResource struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec LocalResourceSpec `json:"spec"`
Status LocalResourceStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// LocalResourceList contains a list of LocalResource.
type LocalResourceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []LocalResource `json:"items"`
}

// GetCondition of this LocalResource.
func (mg *LocalResource) GetCondition(ct prv1.ConditionType) prv1.Condition {
return mg.Status.GetCondition(ct)
}

// SetConditions of this LocalResource.
func (mg *LocalResource) SetConditions(c ...prv1.Condition) {
mg.Status.SetConditions(c...)
}

// GetItems of this LocalResourceList.
func (l *LocalResourceList) GetItems() []resource.Managed {
items := make([]resource.Managed, len(l.Items))
for i := range l.Items {
items[i] = &l.Items[i]
}
return items
}
Loading