Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
- The diff command should strive for accuracy above all else, so in cases where errors or guesses may compromise accuracy, we should fail the diff completely.
- We should not emit partial results for a given XR; if given multiple XRs, it's okay to emit results only for those that pass so long as we call attention to the others that failed.
- We should always emit useful logging with appropriate contextual objects attached.
- the earthly `reviewable` command should always be run with a long timeout. it runs all of our ITs and they can take several minutes.
- the earthly `reviewable` command should always be run with a long timeout. it runs all of our ITs and they can take several minutes.
- in the e2es, every test composition needs to end with function-auto-ready in order for the setup and teardown to work correctly. this is what causes status conditions to be bubbled up from child resources.
9 changes: 6 additions & 3 deletions cmd/diff/client/kubernetes/schema_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ func TestSchemaClient_LoadCRDsFromXRDs(t *testing.T) {
xrd := tu.NewXRD(testXResourcePlural+".example.org", testExampleOrgGroup, testXResourceKind).
WithPlural(testXResourcePlural).
WithSingular("xresource").
WithDefaultVersion().
WithRawSchema([]byte(`{
"type": "object",
"properties": {
Expand All @@ -358,6 +359,7 @@ func TestSchemaClient_LoadCRDsFromXRDs(t *testing.T) {
tu.NewCRD(testXResourcePlural+".example.org", testExampleOrgGroup, testXResourceKind).
WithPlural(testXResourcePlural).
WithSingular("xresource").
WithDefaultVersion().
Build())
correspondingCRD := &un.Unstructured{Object: correspondingCRDObj}

Expand Down Expand Up @@ -508,6 +510,7 @@ func TestSchemaClient_GetCRDByName(t *testing.T) {
testCRD := tu.NewCRD(testCRDName, testExampleOrgGroup, testXResourceKind).
WithPlural(testXResourcePlural).
WithSingular("xresource").
WithDefaultVersion().
Build()

tests := map[string]struct {
Expand All @@ -534,7 +537,7 @@ func TestSchemaClient_GetCRDByName(t *testing.T) {
"DifferentCRDInCache": {
reason: "Should return error when searching for CRD that doesn't exist",
setupCRDs: []*extv1.CustomResourceDefinition{
tu.NewCRD("other."+testExampleOrgGroup, testExampleOrgGroup, "OtherKind").Build(),
tu.NewCRD("other."+testExampleOrgGroup, testExampleOrgGroup, "OtherKind").WithDefaultVersion().Build(),
},
searchName: testCRDName,
expectError: true,
Expand Down Expand Up @@ -596,8 +599,8 @@ func TestSchemaClient_GetCRDByName(t *testing.T) {

func TestSchemaClient_GetAllCRDs(t *testing.T) {
// Create test CRDs
crd1 := tu.NewCRD("crd1."+testExampleOrgGroup, testExampleOrgGroup, "TestKind1").Build()
crd2 := tu.NewCRD("crd2."+testExampleOrgGroup, testExampleOrgGroup, "TestKind2").Build()
crd1 := tu.NewCRD("crd1."+testExampleOrgGroup, testExampleOrgGroup, "TestKind1").WithDefaultVersion().Build()
crd2 := tu.NewCRD("crd2."+testExampleOrgGroup, testExampleOrgGroup, "TestKind2").WithDefaultVersion().Build()

tests := map[string]struct {
reason string
Expand Down
20 changes: 6 additions & 14 deletions cmd/diff/cmd_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,6 @@ import (
//nolint:gochecknoglobals // Required for global serialization across processors
var globalRenderMutex sync.Mutex

// CommonCmdFields contains common fields shared by both XR and Comp commands.
type CommonCmdFields struct {
// Configuration options
NoColor bool `help:"Disable colorized output." name:"no-color"`
Compact bool `help:"Show compact diffs with minimal context." name:"compact"`
Timeout time.Duration `default:"1m" help:"How long to run before timing out."`
QPS float32 `default:"0" help:"Maximum QPS to the API server."`
Burst int `default:"0" help:"Maximum burst for throttle."`
}

// initializeSharedDependencies handles the common initialization logic for both commands.
func initializeSharedDependencies(ctx *kong.Context, log logging.Logger, config *rest.Config, fields CommonCmdFields) (*AppContext, error) {
config = initRestConfig(config, log, fields)
Expand Down Expand Up @@ -102,10 +92,12 @@ func initializeAppContext(timeout time.Duration, appCtx *AppContext, log logging
}

// defaultProcessorOptions returns the standard default options used by both XR and composition processors.
// Call sites can append additional options or override these defaults as needed.
func defaultProcessorOptions() []dp.ProcessorOption {
// This is the single source of truth for behavior defaults in the CLI layer.
func defaultProcessorOptions(fields CommonCmdFields, namespace string) []dp.ProcessorOption {
return []dp.ProcessorOption{
dp.WithColorize(true),
dp.WithCompact(false),
dp.WithNamespace(namespace),
dp.WithColorize(!fields.NoColor),
dp.WithCompact(fields.Compact),
dp.WithMaxNestedDepth(fields.MaxNestedDepth),
}
}
11 changes: 7 additions & 4 deletions cmd/diff/comp.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,16 @@ func (c *CompCmd) initializeDependencies(ctx *kong.Context, log logging.Logger,
}

func makeDefaultCompProc(c *CompCmd, ctx *AppContext, log logging.Logger) dp.CompDiffProcessor {
// Use provided namespace or default to "default"
namespace := c.Namespace
if namespace == "" {
namespace = "default"
}

// Both processors share the same options since they're part of the same command
opts := defaultProcessorOptions()
opts := defaultProcessorOptions(c.CommonCmdFields, namespace)
opts = append(opts,
dp.WithNamespace(c.Namespace),
dp.WithLogger(log),
dp.WithColorize(!c.NoColor), // Override default if NoColor is set
dp.WithCompact(c.Compact), // Override default if Compact is set
dp.WithRenderMutex(&globalRenderMutex),
)

Expand Down
126 changes: 126 additions & 0 deletions cmd/diff/diff_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,132 @@ Summary: 1 added`,
expectedError: false,
noColor: true,
},
"New nested XR creates child XR and downstream resources": {
setupFiles: []string{
// XRDs for parent and child
"testdata/diff/resources/nested/parent-xrd.yaml",
"testdata/diff/resources/nested/child-xrd.yaml",
// Compositions for parent and child
"testdata/diff/resources/nested/parent-composition.yaml",
"testdata/diff/resources/nested/child-composition.yaml",
// XRD for downstream managed resource
"testdata/diff/resources/xdownstreamenvresource-xrd.yaml",
"testdata/diff/resources/functions.yaml",
},
inputFiles: []string{"testdata/diff/new-nested-xr.yaml"},
expectedOutput: `
+++ XChildResource/test-parent-child
+ apiVersion: ns.nested.example.org/v1alpha1
+ kind: XChildResource
+ metadata:
+ annotations:
+ crossplane.io/composition-resource-name: child-xr
+ labels:
+ crossplane.io/composite: test-parent
+ name: test-parent-child
+ namespace: default
+ spec:
+ childField: parent-value

---
+++ XDownstreamResource/test-parent-child-managed
+ apiVersion: ns.nop.example.org/v1alpha1
+ kind: XDownstreamResource
+ metadata:
+ annotations:
+ crossplane.io/composition-resource-name: managed-resource
+ labels:
+ crossplane.io/composite: test-parent-child
+ name: test-parent-child-managed
+ namespace: default
+ spec:
+ forProvider:
+ configData: parent-value

---
+++ XParentResource/test-parent
+ apiVersion: ns.nested.example.org/v1alpha1
+ kind: XParentResource
+ metadata:
+ name: test-parent
+ namespace: default
+ spec:
+ parentField: parent-value

---

Summary: 3 added`,
expectedError: false,
noColor: true,
},
"Modified nested XR propagates changes through child XR to downstream resources": {
setupFiles: []string{
// XRDs for parent and child
"testdata/diff/resources/nested/parent-xrd.yaml",
"testdata/diff/resources/nested/child-xrd.yaml",
// Compositions for parent and child
"testdata/diff/resources/nested/parent-composition.yaml",
"testdata/diff/resources/nested/child-composition.yaml",
// XRD for downstream managed resource
"testdata/diff/resources/xdownstreamenvresource-xrd.yaml",
"testdata/diff/resources/functions.yaml",
// Existing resources
"testdata/diff/resources/nested/existing-parent-xr.yaml",
"testdata/diff/resources/nested/existing-child-xr.yaml",
"testdata/diff/resources/nested/existing-managed-resource.yaml",
},
inputFiles: []string{"testdata/diff/modified-nested-xr.yaml"},
expectedOutput: `
~~~ XChildResource/test-parent-child
apiVersion: ns.nested.example.org/v1alpha1
kind: XChildResource
metadata:
+ annotations:
+ crossplane.io/composition-resource-name: child-xr
+ labels:
+ crossplane.io/composite: test-parent
name: test-parent-child
namespace: default
spec:
- childField: existing-value
+ childField: modified-value

---
~~~ XDownstreamResource/test-parent-child-managed
apiVersion: ns.nop.example.org/v1alpha1
kind: XDownstreamResource
metadata:
annotations:
- gotemplating.fn.crossplane.io/composition-resource-name: managed-resource
+ crossplane.io/composition-resource-name: managed-resource
+ gotemplating.fn.crossplane.io/composition-resource-name: managed-resource
+ labels:
+ crossplane.io/composite: test-parent-child
name: test-parent-child-managed
namespace: default
spec:
forProvider:
- configData: existing-value
+ configData: modified-value


---
~~~ XParentResource/test-parent
apiVersion: ns.nested.example.org/v1alpha1
kind: XParentResource
metadata:
name: test-parent
namespace: default
spec:
- parentField: existing-value
+ parentField: modified-value

---

Summary: 3 modified`,
expectedError: false,
noColor: true,
},
}

runIntegrationTest(t, XRDiffTest, tests)
Expand Down
Loading
Loading