pull/31781/merge
Mujib 3 days ago committed by GitHub
commit 22542fb356
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -447,11 +447,18 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
return nil, err
}
if _, err := i.cfg.KubeClient.Create(
resourceList,
kube.ClientCreateOptionServerSideApply(i.ServerSideApply, false)); err != nil && !apierrors.IsAlreadyExists(err) {
exists, err := namespaceExists(resourceList)
if err != nil {
return nil, err
}
if !exists {
if _, err := i.cfg.KubeClient.Create(
resourceList,
kube.ClientCreateOptionServerSideApply(i.ServerSideApply, false)); err != nil && !apierrors.IsAlreadyExists(err) {
return nil, err
}
}
}
// If Replace is true, we need to supersede the last release.
@ -476,6 +483,29 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
return rel, err
}
func namespaceExists(resourceList kube.ResourceList) (bool, error) {
exists := false
err := resourceList.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
}
_, err = resource.NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name)
if apierrors.IsNotFound(err) {
return nil
}
if err != nil {
return err
}
exists = true
return nil
})
return exists, err
}
func (i *Install) performInstallCtx(ctx context.Context, rel *release.Release, toBeAdopted kube.ResourceList, resources kube.ResourceList) (*release.Release, error) {
type Msg struct {
r *release.Release

@ -35,6 +35,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -1294,3 +1295,155 @@ func TestInstallRelease_WaitOptionsPassedDownstream(t *testing.T) {
// Verify that WaitOptions were passed to GetWaiter
is.NotEmpty(failer.RecordedWaitOptions, "WaitOptions should be passed to GetWaiter")
}
func fakeNamespaceResourceList(name string, statusCode int) kube.ResourceList {
body := kuberuntime.Object(&v1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
"custom-label": "keep-me",
},
Annotations: map[string]string{
"custom-annotation": "keep-me",
},
},
})
if statusCode == http.StatusNotFound {
body = &metav1.Status{
Status: metav1.StatusFailure,
Reason: metav1.StatusReasonNotFound,
Code: http.StatusNotFound,
}
}
restClient := &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
Client: fake.CreateHTTPClient(func(_ *http.Request) (*http.Response, error) {
return newNamespaceResponse(statusCode, body), nil
}),
}
return kube.ResourceList{
&resource.Info{
Client: restClient,
Mapping: &meta.RESTMapping{
Resource: schema.GroupVersionResource{
Version: "v1",
Resource: "namespaces",
},
GroupVersionKind: schema.GroupVersionKind{
Version: "v1",
Kind: "Namespace",
},
Scope: meta.RESTScopeRoot,
},
Name: name,
Object: &v1.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: name},
},
},
}
}
func newNamespaceResponse(code int, obj kuberuntime.Object) *http.Response {
body := kuberuntime.EncodeOrDie(
scheme.Codecs.LegacyCodec(schema.GroupVersion{Version: "v1"}),
obj,
)
return &http.Response{
StatusCode: code,
Header: map[string][]string{
"Content-Type": {"application/json"},
},
Body: io.NopCloser(strings.NewReader(body)),
}
}
type namespaceInstallKubeClient struct {
*kubefake.FailingKubeClient
namespaceStatusCode int
namespaceCreateCalled bool
}
func (c *namespaceInstallKubeClient) Build(reader io.Reader, _ bool) (kube.ResourceList, error) {
b, err := io.ReadAll(reader)
if err != nil {
return nil, err
}
if strings.Contains(string(b), "kind: Namespace") {
return fakeNamespaceResourceList("spaced", c.namespaceStatusCode), nil
}
return kube.ResourceList{}, nil
}
func (c *namespaceInstallKubeClient) Create(resources kube.ResourceList, _ ...kube.ClientCreateOption) (*kube.Result, error) {
for _, info := range resources {
if info.Name == "spaced" && info.Mapping != nil && info.Mapping.GroupVersionKind.Kind == "Namespace" {
c.namespaceCreateCalled = true
}
}
return &kube.Result{}, nil
}
func TestInstall_CreateNamespaceAlreadyExistsSkipsCreate(t *testing.T) {
is := assert.New(t)
config := actionConfigFixture(t)
client := &namespaceInstallKubeClient{
FailingKubeClient: &kubefake.FailingKubeClient{
PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard},
},
namespaceStatusCode: http.StatusOK,
}
config.KubeClient = client
instAction := installActionWithConfig(config)
instAction.CreateNamespace = true
instAction.Namespace = "spaced"
instAction.ServerSideApply = true
resi, err := instAction.Run(buildChart(), nil)
is.NoError(err)
res, err := releaserToV1Release(resi)
is.NoError(err)
is.Equal("spaced", res.Namespace)
is.Equal("Install complete", res.Info.Description)
is.False(client.namespaceCreateCalled, "existing namespace should not be created/applied again")
}
func TestInstall_CreateNamespaceNotExistsCreatesNamespace(t *testing.T) {
is := assert.New(t)
config := actionConfigFixture(t)
client := &namespaceInstallKubeClient{
FailingKubeClient: &kubefake.FailingKubeClient{
PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard},
},
namespaceStatusCode: http.StatusNotFound,
}
config.KubeClient = client
instAction := installActionWithConfig(config)
instAction.CreateNamespace = true
instAction.Namespace = "spaced"
instAction.ServerSideApply = true
resi, err := instAction.Run(buildChart(), nil)
is.NoError(err)
res, err := releaserToV1Release(resi)
is.NoError(err)
is.Equal("spaced", res.Namespace)
is.Equal("Install complete", res.Info.Description)
is.True(client.namespaceCreateCalled, "missing namespace should be created")
}

Loading…
Cancel
Save