Repository: vmware-tanzu/octant
Pull request #2699 (2021-07-30 15:46:07)

Fix helper icon color and namespace switcher css
**What this PR does / why we need it**:
![image](https://user-images.githubusercontent.com/10288252/127678161-8b097945-c151-45a6-a909-2c516addf72b.png)


**Which issue(s) this PR fixes**
https://kubernetes.slack.com/archives/CM37M9FCG/p1627586695140100

Signed-off-by: Sam Foo <foos@vmware.com>



Patch:
diff --git a/changelogs/unreleased/2641-ftovaro b/changelogs/unreleased/2641-ftovaro
new file mode 100644
index 00000000..acf8d61e
--- /dev/null
+++ b/changelogs/unreleased/2641-ftovaro
@@ -0,0 +1,1 @@
+Added `PVC` to object status
diff --git a/internal/objectstatus/objectstatus.go b/internal/objectstatus/objectstatus.go
index c2548944..b600389b 100644
--- a/internal/objectstatus/objectstatus.go
+++ b/internal/objectstatus/objectstatus.go
@@ -40,6 +40,7 @@ var (
 		{apiVersion: "v1", kind: "ReplicationController"}:             replicationController,
 		{apiVersion: "v1", kind: "Service"}:                           service,
 		{apiVersion: "v1", kind: "PersistentVolume"}:                  persistentVolume,
+		{apiVersion: "v1", kind: "PersistentVolumeClaim"}:             persistentVolumeClaim,
 		{apiVersion: "networking.k8s.io/v1", kind: "Ingress"}:         runIngressStatus,
 		{apiVersion: "apiregistration.k8s.io/v1", kind: "APIService"}: apiService,
 	}
diff --git a/internal/objectstatus/persistent_volume_claim.go b/internal/objectstatus/persistent_volume_claim.go
new file mode 100644
index 00000000..4aeb4ddf
--- /dev/null
+++ b/internal/objectstatus/persistent_volume_claim.go
@@ -0,0 +1,42 @@
+/*
+Copyright (c) 2021 the Octant contributors. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package objectstatus
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/client-go/kubernetes/scheme"
+
+	"github.com/vmware-tanzu/octant/internal/link"
+	"github.com/vmware-tanzu/octant/pkg/store"
+	"github.com/vmware-tanzu/octant/pkg/view/component"
+)
+
+func persistentVolumeClaim(_ context.Context, object runtime.Object, _ store.Store, _ link.Interface) (ObjectStatus, error) {
+	if object == nil {
+		return ObjectStatus{}, errors.New("persistent volume claim is nil")
+	}
+
+	pvc := &corev1.PersistentVolumeClaim{}
+
+	if err := scheme.Scheme.Convert(object, pvc, 0); err != nil {
+		return ObjectStatus{}, fmt.Errorf("convert object to v1 PersistentVolumeClaim: %w", err)
+	}
+
+	if pvc.Status.Phase == "Pending" {
+		return ObjectStatus{
+			NodeStatus: component.NodeStatusWarning,
+			Details:    []component.Component{component.NewText("PVC cannot be found")}}, nil
+	}
+
+	return ObjectStatus{
+		NodeStatus: component.NodeStatusOK,
+		Details:    []component.Component{component.NewText("v1 PersistentVolumeClaim is OK")}}, nil
+}
diff --git a/web/src/app/modules/sugarloaf/components/smart/container/container.component.scss b/web/src/app/modules/sugarloaf/components/smart/container/container.component.scss
index ada53028..2ba0903d 100644
--- a/web/src/app/modules/sugarloaf/components/smart/container/container.component.scss
+++ b/web/src/app/modules/sugarloaf/components/smart/container/container.component.scss
@@ -112,23 +112,16 @@
     .header-nav {
       padding-left: 1rem;
 
-      .namespace-switcher,
       .input-filter {
-        margin-right: 10px;
+        z-index: 999; // clarity's data-grid beats this
+        width: calc(150px + (300 - 150) * ((100vw - 500px) / (1920 - 500)));
       }
+    }
 
+    .header-actions {
       .namespace-switcher {
-        display: flex;
-        flex-direction: column;
-        justify-content: center;
-        align-items: center;
         margin-right: 20px;
       }
-
-      .input-filter {
-        z-index: 999; // clarity's data-grid beats this
-        width: calc(150px + (300 - 150) * ((100vw - 500px) / (1920 - 500)));
-      }
     }
   }
 
diff --git a/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.html b/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.html
index d659b1a1..2da2b29d 100644
--- a/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.html
+++ b/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.html
@@ -8,11 +8,12 @@
       [(ngModel)]="inputValue"
       (keyup.enter)="onEnter()"
     />
-    <clr-icon
+    <cds-icon
       class="down-icon"
       (click)="toggleTagList()"
-      shape="caret down"
-    ></clr-icon>
+      shape="angle"
+      direction="down"
+    ></cds-icon>
   </div>
   <ng-container *ngIf="showTagList">
     <div class="input-filter-tags">
@@ -26,11 +27,12 @@
             *ngFor="let filter of filters; trackBy: identifyFilter"
           >
             {{ filter.key }}:{{ filter.value }}
-            <clr-icon
+            <cds-icon
               class="input-filter-tag-remove"
               shape="times"
+              size="sm"
               (click)="remove(filter)"
-            ></clr-icon>
+            ></cds-icon>
           </div>
         </div>
         <div class="clear-all" (click)="clearAllFilters()">clear all</div>
diff --git a/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.scss b/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.scss
index 75013c9b..a2eb30bf 100644
--- a/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.scss
+++ b/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.scss
@@ -82,8 +82,7 @@
 
       .input-filter-tag-remove {
         cursor: pointer;
-        height: 23px;
-        width: 12px;
+        color: var(--cds-global-typography-color-500);
       }
     }
 
diff --git a/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.ts b/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.ts
index 3c8c6201..353ef8a5 100644
--- a/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.ts
+++ b/web/src/app/modules/sugarloaf/components/smart/input-filter/input-filter.component.ts
@@ -13,6 +13,7 @@ import {
   Filter,
   LabelFilterService,
 } from '../../../../shared/services/label-filter/label-filter.service';
+import { ClarityIcons, angleIcon, timesIcon } from '@cds/core/icon';
 import { Subscription } from 'rxjs';
 
 @Component({
@@ -30,7 +31,10 @@ export class InputFilterComponent implements OnInit, OnDestroy {
   constructor(
     private eRef: ElementRef,
     private labelFilterService: LabelFilterService
-  ) {}
+  ) {
+    ClarityIcons.addIcons(angleIcon);
+    ClarityIcons.addIcons(timesIcon);
+  }
 
   ngOnInit() {
     this.labelFilterSubscription = this.labelFilterService.filters.subscribe(
diff --git a/web/src/styles.scss b/web/src/styles.scss
index d623bbd4..a0440601 100644
--- a/web/src/styles.scss
+++ b/web/src/styles.scss
@@ -46,3 +46,7 @@ cds-modal-content app-view-text {
   color: var(--cds-global-typography-color-500);
 }
 
+cds-icon {
+  color: var(--clr-header-font-color);
+}
+


Test patch:
diff --git a/internal/objectstatus/persistent_volume_claim_test.go b/internal/objectstatus/persistent_volume_claim_test.go
new file mode 100644
index 00000000..073ed357
--- /dev/null
+++ b/internal/objectstatus/persistent_volume_claim_test.go
@@ -0,0 +1,99 @@
+/*
+Copyright (c) 2021 the Octant contributors. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package objectstatus
+
+import (
+	"context"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	storefake "github.com/vmware-tanzu/octant/pkg/store/fake"
+
+	linkFake "github.com/vmware-tanzu/octant/internal/link/fake"
+
+	"github.com/golang/mock/gomock"
+
+	"github.com/vmware-tanzu/octant/internal/testutil"
+	"github.com/vmware-tanzu/octant/pkg/view/component"
+
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+func Test_pvc(t *testing.T) {
+	cases := []struct {
+		name     string
+		init     func(*testing.T) runtime.Object
+		expected ObjectStatus
+		isErr    bool
+	}{
+		{
+			name: "in general",
+			init: func(t *testing.T) runtime.Object {
+				objectFile := "pvc_ok.yaml"
+				return testutil.LoadObjectFromFile(t, objectFile)
+			},
+			expected: ObjectStatus{
+				NodeStatus: component.NodeStatusOK,
+				Details: []component.Component{
+					component.NewText("v1 PersistentVolumeClaim is OK"),
+				},
+				Properties: nil,
+			},
+		},
+		{
+			name: "pvc is pending",
+			init: func(t *testing.T) runtime.Object {
+				objectFile := "pvc_pending.yaml"
+				return testutil.LoadObjectFromFile(t, objectFile)
+			},
+			expected: ObjectStatus{
+				NodeStatus: component.NodeStatusWarning,
+				Details: []component.Component{
+					component.NewText("PVC cannot be found"),
+				},
+				Properties: nil,
+			},
+		},
+		{
+			name: "object is nil",
+			init: func(t *testing.T) runtime.Object {
+				return nil
+			},
+			isErr: true,
+		},
+		{
+			name: "object is not a pvc",
+			init: func(t *testing.T) runtime.Object {
+				return &unstructured.Unstructured{}
+			},
+			isErr: true,
+		},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			controller := gomock.NewController(t)
+			linkInterface := linkFake.NewMockInterface(controller)
+			defer controller.Finish()
+
+			o := storefake.NewMockStore(controller)
+			object := tc.init(t)
+			ctx := context.Background()
+
+			status, err := persistentVolumeClaim(ctx, object, o, linkInterface)
+			if tc.isErr {
+				require.Error(t, err)
+				return
+			}
+			require.NoError(t, err)
+
+			assert.Equal(t, tc.expected, status)
+		})
+	}
+}
diff --git a/internal/objectstatus/testdata/pvc_ok.yaml b/internal/objectstatus/testdata/pvc_ok.yaml
new file mode 100644
index 00000000..d7dbe0ec
--- /dev/null
+++ b/internal/objectstatus/testdata/pvc_ok.yaml
@@ -0,0 +1,31 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  annotations:
+    kubectl.kubernetes.io/last-applied-configuration: |
+      {"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{},"name":"database-persistent-volume-claim","namespace":"default"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"2Gi"}}}}
+    pv.kubernetes.io/bind-completed: "yes"
+    pv.kubernetes.io/bound-by-controller: "yes"
+    volume.beta.kubernetes.io/storage-provisioner: k8s.io/minikube-hostpath
+  creationTimestamp: "2021-07-27T17:48:53Z"
+  finalizers:
+    - kubernetes.io/pvc-protection
+  name: database-persistent-volume-claim
+  namespace: default
+  resourceVersion: "429431"
+  uid: aec2068c-9fd6-4735-8ee4-33a4f4ce3497
+spec:
+  accessModes:
+    - ReadWriteOnce
+  resources:
+    requests:
+      storage: 2Gi
+  storageClassName: standard
+  volumeMode: Filesystem
+  volumeName: pvc-aec2068c-9fd6-4735-8ee4-33a4f4ce3497
+status:
+  accessModes:
+    - ReadWriteOnce
+  capacity:
+    storage: 2Gi
+  phase: Bound
diff --git a/internal/objectstatus/testdata/pvc_pending.yaml b/internal/objectstatus/testdata/pvc_pending.yaml
new file mode 100644
index 00000000..a7c0443b
--- /dev/null
+++ b/internal/objectstatus/testdata/pvc_pending.yaml
@@ -0,0 +1,24 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  annotations:
+    kubectl.kubernetes.io/last-applied-configuration: |
+      {"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{},"name":"www-web-0","namespace":"default"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"2Gi"}},"storageClassName":"my-storage-class"}}
+  creationTimestamp: "2021-07-27T17:52:55Z"
+  finalizers:
+    - kubernetes.io/pvc-protection
+  name: www-web-0
+  namespace: default
+  resourceVersion: "429650"
+  uid: c214c59e-38fa-4fe2-bab8-8b11432549ca
+spec:
+  accessModes:
+    - ReadWriteOnce
+  resources:
+    requests:
+      storage: 2Gi
+  storageClassName: my-storage-class
+  volumeMode: Filesystem
+status:
+  phase: Pending
+