pull/32070/merge
Tarun Gupta Akirala 1 day ago committed by GitHub
commit 7204703226
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -518,7 +518,7 @@ func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.Resource
if len(toBeAdopted) == 0 && len(resources) > 0 {
_, err = i.cfg.KubeClient.Create(
resources,
kube.ClientCreateOptionServerSideApply(i.ServerSideApply, false))
kube.ClientCreateOptionServerSideApply(i.ServerSideApply, i.ForceConflicts))
} else if len(resources) > 0 {
updateThreeWayMergeForUnstructured := i.TakeOwnership && !i.ServerSideApply // Use three-way merge when taking ownership (and not using server-side apply)
_, err = i.cfg.KubeClient.Update(

@ -159,6 +159,41 @@ func createDummyCRDList(owned bool) kube.ResourceList {
return resourceList
}
func createNewResourceList() kube.ResourceList {
obj := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "dummyName",
Namespace: "spaced",
},
}
resInfo := resource.Info{
Name: "dummyName",
Namespace: "spaced",
Mapping: &meta.RESTMapping{
Resource: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployment"},
GroupVersionKind: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
Scope: meta.RESTScopeNamespace,
},
Object: obj,
}
resInfo.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "apps", Version: "v1"},
NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
Client: fake.CreateHTTPClient(func(_ *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: http.StatusNotFound,
Header: http.Header{"Content-Type": []string{kuberuntime.ContentTypeJSON}},
Body: io.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","reason":"NotFound","code":404}`))),
}, nil
}),
}
var resourceList kube.ResourceList
resourceList.Append(&resInfo)
return resourceList
}
func installActionWithConfig(config *Configuration) *Install {
instAction := NewInstall(config)
instAction.Namespace = "spaced"
@ -1297,3 +1332,53 @@ func TestInstallRelease_WaitOptionsPassedDownstream(t *testing.T) {
// Verify that WaitOptions were passed to GetWaiter
is.NotEmpty(failer.RecordedWaitOptions, "WaitOptions should be passed to GetWaiter")
}
func TestInstallRelease_ForceConflictsPassedToCreate(t *testing.T) {
is := assert.New(t)
// Use resources whose REST client returns NotFound so they go through
// the Create path (toBeAdopted is empty) in performInstall.
config := actionConfigFixtureWithDummyResources(t, createNewResourceList())
instAction := installActionWithConfig(config)
instAction.ServerSideApply = true
instAction.ForceConflicts = true
failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
vals := map[string]any{}
_, err := instAction.Run(buildChart(), vals)
is.NoError(err)
is.NotEmpty(failer.RecordedCreateCalls, "Create calls should be recorded")
var foundForceConflicts bool
for _, call := range failer.RecordedCreateCalls {
resolved, err := kube.ResolveCreateOptions(call...)
is.NoError(err)
if resolved.ForceConflicts {
foundForceConflicts = true
}
}
is.True(foundForceConflicts, "ForceConflicts should be passed through on the Create path")
}
func TestInstallRelease_ForceConflictsFalseByDefault(t *testing.T) {
is := assert.New(t)
config := actionConfigFixtureWithDummyResources(t, createNewResourceList())
instAction := installActionWithConfig(config)
instAction.ServerSideApply = true
instAction.ForceConflicts = false
failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
vals := map[string]any{}
_, err := instAction.Run(buildChart(), vals)
is.NoError(err)
is.NotEmpty(failer.RecordedCreateCalls, "Create calls should be recorded")
for _, call := range failer.RecordedCreateCalls {
resolved, err := kube.ResolveCreateOptions(call...)
is.NoError(err)
is.False(resolved.ForceConflicts, "ForceConflicts should be false when not set")
}
}

@ -309,6 +309,28 @@ func ClientCreateOptionFieldValidationDirective(fieldValidationDirective FieldVa
}
}
// ResolvedCreateOptions holds the resolved values from ClientCreateOption functions.
// This is exported for testing purposes only.
type ResolvedCreateOptions struct {
ServerSideApply bool
ForceConflicts bool
}
// ResolveCreateOptions applies the given ClientCreateOptions and returns the resolved values.
// This is exported for testing purposes only.
func ResolveCreateOptions(opts ...ClientCreateOption) (ResolvedCreateOptions, error) {
o := clientCreateOptions{}
for _, opt := range opts {
if err := opt(&o); err != nil {
return ResolvedCreateOptions{}, err
}
}
return ResolvedCreateOptions{
ServerSideApply: o.serverSideApply,
ForceConflicts: o.forceConflicts,
}, nil
}
func (c *Client) makeCreateApplyFunc(serverSideApply, forceConflicts, dryRun bool, fieldValidationDirective FieldValidationDirective) CreateApplyFunc {
if serverSideApply {
c.Logger().Debug(

@ -49,6 +49,8 @@ type FailingKubeClient struct {
WaitDuration time.Duration
// RecordedWaitOptions stores the WaitOptions passed to GetWaiter for testing
RecordedWaitOptions []kube.WaitOption
// RecordedCreateCalls stores the ClientCreateOptions for each call to Create for testing
RecordedCreateCalls [][]kube.ClientCreateOption
}
var _ kube.Interface = &FailingKubeClient{}
@ -65,6 +67,7 @@ type FailingKubeWaiter struct {
// Create returns the configured error if set or prints
func (f *FailingKubeClient) Create(resources kube.ResourceList, options ...kube.ClientCreateOption) (*kube.Result, error) {
f.RecordedCreateCalls = append(f.RecordedCreateCalls, options)
if f.CreateError != nil {
return nil, f.CreateError
}

Loading…
Cancel
Save