-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds ResourceGroupPathManifestReader
- Loading branch information
Showing
3 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright 2020 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package live | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/GoogleContainerTools/kpt/pkg/kptfile/kptfileutil" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/cli-runtime/pkg/resource" | ||
"k8s.io/klog" | ||
"sigs.k8s.io/cli-utils/pkg/common" | ||
"sigs.k8s.io/cli-utils/pkg/manifestreader" | ||
) | ||
|
||
// ResourceGroupPathManifestReader encapsulates the default path | ||
// manifest reader. | ||
type ResourceGroupPathManifestReader struct { | ||
pathReader *manifestreader.PathManifestReader | ||
} | ||
|
||
// Read reads the manifests and returns them as Info objects. | ||
// Generates and adds a ResourceGroup inventory object from | ||
// Kptfile data. | ||
func (p *ResourceGroupPathManifestReader) Read() ([]*resource.Info, error) { | ||
// Using the default path reader to generate the objects. | ||
infos, err := p.pathReader.Read() | ||
if err != nil { | ||
return []*resource.Info{}, err | ||
} | ||
// Read the Kptfile in the top directory to get the inventory | ||
// parameters to create the ResourceGroup inventory object. | ||
kf, err := kptfileutil.ReadFile(p.pathReader.Path) | ||
if err != nil { | ||
return []*resource.Info{}, err | ||
} | ||
inv := kf.Inventory | ||
klog.V(4).Infof("generating ResourceGroup inventory object %s/%s/%s", inv.Namespace, inv.Name, inv.InventoryID) | ||
invInfo, err := generateInventoryObj(inv.Name, inv.Namespace, inv.InventoryID) | ||
if err != nil { | ||
return []*resource.Info{}, err | ||
} | ||
infos = append(infos, invInfo) | ||
return infos, nil | ||
} | ||
|
||
// generateInventoryObj returns the ResourceGroupInventory object using the | ||
// passed information. | ||
func generateInventoryObj(name string, namespace string, id string) (*resource.Info, error) { | ||
// Validate the parameters | ||
name = strings.TrimSpace(name) | ||
if name == "" { | ||
return nil, fmt.Errorf("kptfile inventory empty name") | ||
} | ||
namespace = strings.TrimSpace(namespace) | ||
if namespace == "" { | ||
return nil, fmt.Errorf("kptfile inventory empty namespace") | ||
} | ||
id = strings.TrimSpace(id) | ||
if id == "" { | ||
return nil, fmt.Errorf("kptfile inventory missing inventoryID") | ||
} | ||
// Create and return ResourceGroup custom resource as inventory object. | ||
var inventoryObj = unstructured.Unstructured{ | ||
Object: map[string]interface{}{ | ||
"apiVersion": "configmanagement.gke.io/v1beta1", | ||
"kind": "ResourceGroup", | ||
"metadata": map[string]interface{}{ | ||
"name": name, | ||
"namespace": namespace, | ||
"labels": map[string]interface{}{ | ||
common.InventoryLabel: id, | ||
}, | ||
}, | ||
"spec": map[string]interface{}{ | ||
"resources": []interface{}{}, | ||
}, | ||
}, | ||
} | ||
var invInfo = &resource.Info{ | ||
Namespace: namespace, | ||
Name: name, | ||
Object: &inventoryObj, | ||
} | ||
return invInfo, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
// Copyright 2020 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package live | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"k8s.io/apimachinery/pkg/api/meta" | ||
"k8s.io/cli-runtime/pkg/resource" | ||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing" | ||
"sigs.k8s.io/cli-utils/pkg/common" | ||
"sigs.k8s.io/cli-utils/pkg/inventory" | ||
"sigs.k8s.io/cli-utils/pkg/manifestreader" | ||
) | ||
|
||
var ( | ||
inventoryNamespace = "test-namespace" | ||
inventoryName = "inventory-obj-name" | ||
inventoryID = "XXXXXXXXXX-FOOOOOO" | ||
) | ||
|
||
var kptFile = ` | ||
apiVersion: kpt.dev/v1alpha1 | ||
kind: Kptfile | ||
metadata: | ||
name: test1 | ||
upstream: | ||
type: git | ||
git: | ||
commit: 786b898857bd7e9647c229d5f39b0be4de86c915 | ||
repo: git@github.com:seans3/blueprint-helloworld | ||
directory: / | ||
ref: master | ||
inventory: | ||
namespace: test-namespace | ||
name: inventory-obj-name | ||
inventoryID: XXXXXXXXXX-FOOOOOO | ||
` | ||
|
||
var kptFileMissingID = ` | ||
apiVersion: kpt.dev/v1alpha1 | ||
kind: Kptfile | ||
metadata: | ||
name: test1 | ||
upstream: | ||
type: git | ||
git: | ||
commit: 786b898857bd7e9647c229d5f39b0be4de86c915 | ||
repo: git@github.com:seans3/blueprint-helloworld | ||
directory: / | ||
ref: master | ||
inventory: | ||
namespace: test-namespace | ||
name: inventory-obj-name | ||
` | ||
|
||
var podA = ` | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: pod-a | ||
namespace: test-namespace | ||
labels: | ||
name: test-pod-label | ||
spec: | ||
containers: | ||
- name: kubernetes-pause | ||
image: k8s.gcr.io/pause:2.0 | ||
` | ||
|
||
var deploymentA = ` | ||
kind: Deployment | ||
apiVersion: apps/v1 | ||
metadata: | ||
name: test-deployment | ||
spec: | ||
replicas: 1 | ||
` | ||
|
||
func TestInvGenPathManifestReader_Read(t *testing.T) { | ||
testCases := map[string]struct { | ||
manifests map[string]string | ||
numInfos int | ||
isError bool | ||
}{ | ||
"Kptfile missing inventory id is error": { | ||
manifests: map[string]string{ | ||
"Kptfile": kptFileMissingID, | ||
"pod-a.yaml": podA, | ||
}, | ||
numInfos: 0, | ||
isError: true, | ||
}, | ||
"Basic ResourceGroup inventory object created": { | ||
manifests: map[string]string{ | ||
"Kptfile": kptFile, | ||
"pod-a.yaml": podA, | ||
}, | ||
numInfos: 2, | ||
isError: false, | ||
}, | ||
"ResourceGroup inventory object created, multiple objects": { | ||
manifests: map[string]string{ | ||
"Kptfile": kptFile, | ||
"pod-a.yaml": podA, | ||
"deployment-a.yaml": deploymentA, | ||
}, | ||
numInfos: 3, | ||
isError: false, | ||
}, | ||
"ResourceGroup inventory object created, Kptfile last": { | ||
manifests: map[string]string{ | ||
"deployment-a.yaml": deploymentA, | ||
"Kptfile": kptFile, | ||
}, | ||
numInfos: 2, | ||
isError: false, | ||
}, | ||
} | ||
|
||
for tn, tc := range testCases { | ||
t.Run(tn, func(t *testing.T) { | ||
tf := cmdtesting.NewTestFactory().WithNamespace("test-ns") | ||
defer tf.Cleanup() | ||
|
||
// Set up the yaml manifests (including Kptfile) in temp dir. | ||
dir, err := ioutil.TempDir("", "path-reader-test") | ||
assert.NoError(t, err) | ||
for filename, content := range tc.manifests { | ||
p := filepath.Join(dir, filename) | ||
err := ioutil.WriteFile(p, []byte(content), 0600) | ||
assert.NoError(t, err) | ||
} | ||
|
||
// Create the ResourceGroupPathManifestReader, and Read() | ||
// the manifests into infos. | ||
pathReader := &manifestreader.PathManifestReader{ | ||
Path: dir, | ||
ReaderOptions: manifestreader.ReaderOptions{ | ||
Factory: tf, | ||
Namespace: inventoryNamespace, | ||
EnforceNamespace: false, | ||
}, | ||
} | ||
rgPathReader := &ResourceGroupPathManifestReader{ | ||
pathReader: pathReader, | ||
} | ||
readInfos, err := rgPathReader.Read() | ||
|
||
// Validate the returned values are correct. | ||
if tc.isError { | ||
if err == nil { | ||
t.Fatalf("expected error but received none") | ||
} | ||
return | ||
} | ||
assert.NoError(t, err) | ||
assert.Equal(t, len(readInfos), tc.numInfos) | ||
for _, info := range readInfos { | ||
assert.Equal(t, inventoryNamespace, info.Namespace) | ||
} | ||
invInfo, _, err := inventory.SplitInfos(readInfos) | ||
assert.NoError(t, err) | ||
assert.Equal(t, inventoryName, invInfo.Name) | ||
actualID, err := getInventoryLabel(invInfo) | ||
assert.NoError(t, err) | ||
assert.Equal(t, inventoryID, actualID) | ||
}) | ||
} | ||
} | ||
|
||
func getInventoryLabel(inv *resource.Info) (string, error) { | ||
obj := inv.Object | ||
if obj == nil { | ||
return "", fmt.Errorf("inventory object is nil") | ||
} | ||
accessor, err := meta.Accessor(obj) | ||
if err != nil { | ||
return "", err | ||
} | ||
labels := accessor.GetLabels() | ||
inventoryLabel, exists := labels[common.InventoryLabel] | ||
if !exists { | ||
return "", fmt.Errorf("inventory label does not exist for inventory object: %s", common.InventoryLabel) | ||
} | ||
return inventoryLabel, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters