Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3dfdad8
PR Controller PoC
efiacor Apr 1, 2026
ea783d8
Fix failing CI checks
efiacor Apr 1, 2026
319f15a
Fix failing upgrade cli test client failure
efiacor Apr 1, 2026
dd4b53a
Add SS field selectors to CRD
efiacor Apr 2, 2026
29aa620
Bump to kpt beta.62
efiacor Apr 2, 2026
2a6a9b6
Add --show-kptfile impl to v1alpha2
efiacor Apr 2, 2026
39804d5
Revert ResolveToImage call signature
efiacor Apr 2, 2026
dc9e1fc
Rebase on main
efiacor Apr 14, 2026
98fc2b6
Update to use kptfilev1.Locator api change
efiacor Apr 14, 2026
aa08536
Address review comments
efiacor Apr 14, 2026
7fa04fb
Add missed finalizer RBAC etc
efiacor Apr 15, 2026
14a2dbf
Add ownerRef and finalizer logic to rpkg ctr
efiacor Apr 15, 2026
7963023
Reimpl v1alpha2 repo filtering on packagecommon
efiacor Apr 16, 2026
3d86429
Fix missed mocks
efiacor Apr 17, 2026
141bb16
Address review comments
efiacor Apr 17, 2026
a04b3ab
Fix SSA field ownership, controller bugs, and lifecycle handling
efiacor Apr 18, 2026
a1979ee
Fix failing tests
efiacor Apr 18, 2026
8d2c180
Add more test coverage
efiacor Apr 18, 2026
0d6ef29
Fix repo resync and add launch
efiacor Apr 20, 2026
6835050
Impl missed DeletePackage fn for git cleanup
efiacor Apr 21, 2026
9e11d7f
Add missed DbPushDraftsToGit flag to ctr cache
efiacor Apr 21, 2026
e688d57
Add missed rebase removal
efiacor Apr 24, 2026
1386352
Address review comments
efiacor Apr 27, 2026
0dac197
Address review comments
efiacor Apr 28, 2026
4b66cd2
Add FnConfigRec wiring to PR Controller
efiacor Apr 29, 2026
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ apiserver.local.config/
default.etcd/

# Local cache files
.cache/
.cache*

# Local backup files
.$*.bkp
Expand Down
3 changes: 3 additions & 0 deletions .mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ packages:
Repository: {}
PackageRevision: {}
PackageRevisionDraft: {}
ContentCache: {}
PackageContent: {}
ExternalPackageFetcher: {}

github.com/nephio-project/porch/pkg/engine:
interfaces:
Expand Down
51 changes: 48 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,60 @@
}
},
{
"name": "Launch Controllers",
"name": "Launch Controllers (repositories)",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/controllers",
"args": [
"--reconcilers=repositories",
"--repositories.cache-directory=${workspaceFolder}/.cache-controller-repo",
"--repositories.max-concurrent-reconciles=100",
"--repositories.max-concurrent-syncs=50",
"--repositories.repo-operation-retry-attempts=3",
"--repositories.health-check-frequency=1m",
"--repositories.full-sync-frequency=3m",
"-v=2"
],
"cwd": "${workspaceFolder}",
"env": {
"GIT_CACHE_DIR": "${workspaceFolder}/.cache-controller-repo",
"DB_HOST": "172.18.255.201",
"DB_PORT": "5432",
"DB_NAME": "porch",
"DB_USER": "porch",
"DB_PASSWORD": "porch",
"DB_DRIVER": "pgx"
}
},
{
"name": "Launch Controllers (repositories + packagerevisions v1alpha2)",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/controllers",
"args": [
"--reconcilers=repositories,packagerevisions",
"--repositories.create-v1alpha2-rpkg=true",
"--repositories.max-concurrent-reconciles=100",
"--repositories.max-concurrent-syncs=50",
"--repositories.cache-directory=${workspaceFolder}/.cache-controller-v1alpha2",
"--repositories.repo-operation-retry-attempts=3",
"--repositories.health-check-frequency=1m",
"--repositories.full-sync-frequency=3m",
"--packagerevisions.repo-operation-retry-attempts=3",
"-v=2"
],
"cwd": "${workspaceFolder}",
"env": {
"ENABLE_PACKAGEVARIANTS": "true",
"ENABLE_PACKAGEVARIANTSETS": "true"
"GIT_CACHE_DIR": "${workspaceFolder}/.cache-controller-v1alpha2",
"DB_HOST": "172.18.255.201",
"DB_PORT": "5432",
"DB_NAME": "porch",
"DB_USER": "porch",
"DB_PASSWORD": "porch",
"DB_DRIVER": "pgx",
"FUNCTION_RUNNER_ADDRESS": "172.18.255.202:9445"
}
},
// A configuration for running a porchctl command using the VS Code debugger.
Expand Down
76 changes: 76 additions & 0 deletions api/porch/v1alpha2/kptdata_conversion.go
Comment thread
liamfallon marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2026 The kpt and Nephio 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 v1alpha2

import (
kptfilev1 "github.com/kptdev/kpt/pkg/api/kptfile/v1"
)

// KptfileToPackageConditions converts Kptfile status conditions to v1alpha2 PackageConditions.
func KptfileToPackageConditions(kf kptfilev1.KptFile) []PackageCondition {
if kf.Status == nil {
return nil
}
conds := make([]PackageCondition, 0, len(kf.Status.Conditions))
for _, c := range kf.Status.Conditions {
conds = append(conds, PackageCondition{
Type: c.Type,
Status: PackageConditionStatus(c.Status),
Reason: c.Reason,
Message: c.Message,
})
}
return conds
}

// KptfileToReadinessGates converts Kptfile readiness gates to v1alpha2 ReadinessGates.
func KptfileToReadinessGates(kf kptfilev1.KptFile) []ReadinessGate {
if kf.Info == nil {
return nil
}
gates := make([]ReadinessGate, 0, len(kf.Info.ReadinessGates))
for _, rg := range kf.Info.ReadinessGates {
gates = append(gates, ReadinessGate{ConditionType: rg.ConditionType})
}
return gates
}

// KptfileToPackageMetadata converts Kptfile labels and annotations to v1alpha2 PackageMetadata.
func KptfileToPackageMetadata(kf kptfilev1.KptFile) *PackageMetadata {
if len(kf.Labels) == 0 && len(kf.Annotations) == 0 {
return nil
}
return &PackageMetadata{
Labels: kf.Labels,
Annotations: kf.Annotations,
}
}

// KptLocatorToLocator converts a kpt Locator to a v1alpha2 Locator.
// The two types are structurally identical but live in different packages.
func KptLocatorToLocator(lock kptfilev1.Locator) *Locator {
if lock.Git == nil {
return nil
}
return &Locator{
Type: OriginType(lock.Type),
Git: &GitLock{
Repo: lock.Git.Repo,
Directory: lock.Git.Directory,
Ref: lock.Git.Ref,
Commit: lock.Git.Commit,
},
}
}
113 changes: 113 additions & 0 deletions api/porch/v1alpha2/kptdata_conversion_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2026 The kpt and Nephio 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 v1alpha2

import (
"testing"

kptfilev1 "github.com/kptdev/kpt/pkg/api/kptfile/v1"
"github.com/stretchr/testify/assert"
)

func TestKptfileToPackageConditions(t *testing.T) {
// nil status
assert.Nil(t, KptfileToPackageConditions(kptfilev1.KptFile{}))

// empty conditions
kf := kptfilev1.KptFile{Status: &kptfilev1.Status{}}
assert.Empty(t, KptfileToPackageConditions(kf))

// populated conditions
kf.Status.Conditions = []kptfilev1.Condition{
{Type: "Ready", Status: kptfilev1.ConditionTrue, Reason: "AllGood", Message: "all good"},
{Type: "Validated", Status: kptfilev1.ConditionFalse, Reason: "Failed", Message: "bad input"},
}
conds := KptfileToPackageConditions(kf)
assert.Len(t, conds, 2)
assert.Equal(t, "Ready", conds[0].Type)
assert.Equal(t, PackageConditionTrue, conds[0].Status)
assert.Equal(t, "AllGood", conds[0].Reason)
assert.Equal(t, "all good", conds[0].Message)
assert.Equal(t, "Validated", conds[1].Type)
assert.Equal(t, PackageConditionFalse, conds[1].Status)
}

func TestKptfileToReadinessGates(t *testing.T) {
// nil info
assert.Nil(t, KptfileToReadinessGates(kptfilev1.KptFile{}))

// empty gates
kf := kptfilev1.KptFile{Info: &kptfilev1.PackageInfo{}}
assert.Empty(t, KptfileToReadinessGates(kf))

// populated gates
kf.Info.ReadinessGates = []kptfilev1.ReadinessGate{
{ConditionType: "Ready"},
{ConditionType: "Validated"},
}
gates := KptfileToReadinessGates(kf)
assert.Len(t, gates, 2)
assert.Equal(t, "Ready", gates[0].ConditionType)
assert.Equal(t, "Validated", gates[1].ConditionType)
}

func TestKptfileToPackageMetadata(t *testing.T) {
// no labels or annotations
assert.Nil(t, KptfileToPackageMetadata(kptfilev1.KptFile{}))

// labels only
kf := kptfilev1.KptFile{}
kf.Labels = map[string]string{"app": "foo"}
meta := KptfileToPackageMetadata(kf)
assert.Equal(t, "foo", meta.Labels["app"])
assert.Nil(t, meta.Annotations)

// annotations only
kf = kptfilev1.KptFile{}
kf.Annotations = map[string]string{"note": "bar"}
meta = KptfileToPackageMetadata(kf)
assert.Nil(t, meta.Labels)
assert.Equal(t, "bar", meta.Annotations["note"])

// both
kf = kptfilev1.KptFile{}
kf.Labels = map[string]string{"app": "foo"}
kf.Annotations = map[string]string{"note": "bar"}
meta = KptfileToPackageMetadata(kf)
assert.Equal(t, "foo", meta.Labels["app"])
assert.Equal(t, "bar", meta.Annotations["note"])
}

func TestKptLocatorToLocator(t *testing.T) {
// nil git
assert.Nil(t, KptLocatorToLocator(kptfilev1.Locator{}))

// populated
lock := kptfilev1.Locator{
Type: kptfilev1.GitOrigin,
Git: &kptfilev1.GitLock{
Repo: "https://github.com/example/repo.git",
Directory: "pkg/foo",
Ref: "v1.0.0",
Commit: "abc123",
},
}
loc := KptLocatorToLocator(lock)
assert.Equal(t, OriginType(kptfilev1.GitOrigin), loc.Type)
assert.Equal(t, "https://github.com/example/repo.git", loc.Git.Repo)
assert.Equal(t, "pkg/foo", loc.Git.Directory)
assert.Equal(t, "v1.0.0", loc.Git.Ref)
assert.Equal(t, "abc123", loc.Git.Commit)
}
46 changes: 35 additions & 11 deletions api/porch/v1alpha2/packagerevision_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ import (
// +kubebuilder:printcolumn:name="Lifecycle",type=string,JSONPath=`.spec.lifecycle`
// +kubebuilder:printcolumn:name="Repository",type=string,JSONPath=`.spec.repository`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:selectablefield:JSONPath=`.spec.lifecycle`
// +kubebuilder:selectablefield:JSONPath=`.spec.repository`
// +kubebuilder:selectablefield:JSONPath=`.spec.packageName`
// +kubebuilder:selectablefield:JSONPath=`.spec.workspaceName`
// +kubebuilder:selectablefield:JSONPath=`.status.revision`
type PackageRevision struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand All @@ -59,6 +64,19 @@ const (
LatestPackageRevisionValue = "true"
)

// AnnotationRenderRequest triggers async rendering when patched by the PRR handler.
const AnnotationRenderRequest = "porch.kpt.dev/render-request"

// PackageRevisionFinalizer prevents deletion of packages that have not been through DeletionProposed.
const PackageRevisionFinalizer = "porch.kpt.dev/packagerevisions"

const (
// PushOnFnRenderFailureKey controls whether resources are written back
// to storage when the render pipeline fails.
PushOnFnRenderFailureKey = "porch.kpt.dev/push-on-render-failure"
PushOnFnRenderFailureValue = "true"
)
Comment thread
efiacor marked this conversation as resolved.

// PkgRevFieldSelector defines field selectors for PackageRevision.
// Requires controller-runtime field indexing setup in controller.
type PkgRevFieldSelector string
Expand Down Expand Up @@ -137,6 +155,10 @@ type ReadinessGate struct {

// PackageRevisionStatus defines the observed state of PackageRevision
type PackageRevisionStatus struct {
// ObservedGeneration is the generation of the PackageRevision spec that was last reconciled.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// Revision identifies the version of the package.
// This is assigned by the system when the package is published.
Revision int `json:"revision,omitempty"`
Expand All @@ -151,7 +173,8 @@ type PackageRevisionStatus struct {
PublishedBy string `json:"publishedBy,omitempty"`

// PublishedAt is the time when the packagerevision were approved.
PublishedAt metav1.Time `json:"publishedAt,omitempty"`
// +optional
PublishedAt *metav1.Time `json:"publishedAt,omitempty"`

// Deployment is true if this is a deployment package (in a deployment repository).
Deployment bool `json:"deployment,omitempty"`
Expand All @@ -173,7 +196,9 @@ type PackageRevisionStatus struct {
// PackageConditions from Kptfile. Set by KRM functions, used for ReadinessGates.
PackageConditions []PackageCondition `json:"packageConditions,omitempty"`

// Conditions for controller state (e.g., Ready).
// Conditions for controller state (e.g., Ready, Rendered).
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

Expand Down Expand Up @@ -220,21 +245,20 @@ const (
)

// Condition types for PackageRevision.Conditions (controller state)
// Additional condition types may be added in future (e.g., Rendered, Validated, Synced)
const (
// ConditionReady indicates the package is ready for use.
// This is a summary condition that aggregates other conditions.
ConditionReady = "Ready"

// ConditionRendered indicates whether the latest content has been rendered.
ConditionRendered = "Rendered"
)

// Condition reasons for PackageRevision.Conditions
const (
// ReasonReady indicates the package is ready
ReasonReady = "Ready"

// ReasonPending indicates the package is pending some operation
ReasonPending = "Pending"

// ReasonFailed indicates an operation failed
ReasonFailed = "Failed"
ReasonReady = "Ready"
ReasonPending = "Pending"
ReasonFailed = "Failed"
ReasonRendered = "Rendered"
ReasonRenderFailed = "RenderFailed"
)
Comment thread
efiacor marked this conversation as resolved.
Loading
Loading