Inventory info helper functions

This commit is contained in:
Sean Sullivan
2020-01-08 12:08:47 -08:00
parent abc57e481b
commit c6cc457f45
2 changed files with 301 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// package kubectlcobra contains cobra commands from kubectl
package kubectlcobra
import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// Separates inventory fields. This string is allowable as a
// ConfigMap key, but it is not allowed as a character in
// resource name.
const fieldSeparator = "_"
// Inventory organizes and stores the indentifying information
// for an object. This struct (as a string) is stored in a
// grouping object to keep track of sets of applied objects.
type Inventory struct {
Namespace string
Name string
GroupKind schema.GroupKind
}
// createInventory returns a pointer to an Inventory struct filled
// with the passed values. This function validates the passed fields
// and returns an error for bad parameters.
func createInventory(namespace string,
name string, gk schema.GroupKind) (*Inventory, error) {
// Namespace can be empty, but name cannot.
name = strings.TrimSpace(name)
if name == "" {
return nil, fmt.Errorf("Empty name for inventory object")
}
if gk.Empty() {
return nil, fmt.Errorf("Empty GroupKind for inventory object")
}
return &Inventory{
Namespace: strings.TrimSpace(namespace),
Name: name,
GroupKind: gk,
}, nil
}
// parseInventory takes a string, splits it into its five fields,
// and returns a pointer to an Inventory struct storing the
// five fields. Example inventory string:
//
// test-namespace/test-name/apps/v1/ReplicaSet
//
// Returns an error if unable to parse and create the Inventory
// struct.
func parseInventory(inv string) (*Inventory, error) {
parts := strings.Split(inv, fieldSeparator)
if len(parts) == 4 {
gk := schema.GroupKind{
Group: strings.TrimSpace(parts[2]),
Kind: strings.TrimSpace(parts[3]),
}
return createInventory(parts[0], parts[1], gk)
}
return nil, fmt.Errorf("Unable to decode inventory: %s\n", inv)
}
// Equals returns true if the Inventory structs are identical;
// false otherwise.
func (i *Inventory) Equals(other *Inventory) bool {
return i.String() == other.String()
}
// String create a string version of the Inventory struct.
func (i *Inventory) String() string {
return fmt.Sprintf("%s%s%s%s%s%s%s",
i.Namespace, fieldSeparator,
i.Name, fieldSeparator,
i.GroupKind.Group, fieldSeparator,
i.GroupKind.Kind)
}

View File

@@ -0,0 +1,218 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// package kubectlcobra contains cobra commands from kubectl
package kubectlcobra
import (
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestCreateInventory(t *testing.T) {
tests := []struct {
namespace string
name string
gk schema.GroupKind
expected string
isError bool
}{
{
namespace: " \n",
name: " test-name\t",
gk: schema.GroupKind{
Group: "apps",
Kind: "ReplicaSet",
},
expected: "_test-name_apps_ReplicaSet",
isError: false,
},
{
namespace: "test-namespace ",
name: " test-name\t",
gk: schema.GroupKind{
Group: "apps",
Kind: "ReplicaSet",
},
expected: "test-namespace_test-name_apps_ReplicaSet",
isError: false,
},
// Error with empty name.
{
namespace: "test-namespace ",
name: " \t",
gk: schema.GroupKind{
Group: "apps",
Kind: "ReplicaSet",
},
expected: "",
isError: true,
},
// Error with empty GroupKind.
{
namespace: "test-namespace",
name: "test-name",
gk: schema.GroupKind{},
expected: "",
isError: true,
},
}
for _, test := range tests {
inv, err := createInventory(test.namespace, test.name, test.gk)
if !test.isError {
if err != nil {
t.Errorf("Error creating inventory when it should have worked.")
} else if test.expected != inv.String() {
t.Errorf("Expected inventory (%s) != created inventory(%s)\n", test.expected, inv.String())
}
}
if test.isError && err == nil {
t.Errorf("Should have returned an error in createInventory()")
}
}
}
func TestInventoryEqual(t *testing.T) {
tests := []struct {
inventory1 *Inventory
inventory2 *Inventory
isEqual bool
}{
// Two equal inventories without a namespace
{
inventory1: &Inventory{
Name: "test-inv",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "Deployment",
},
},
inventory2: &Inventory{
Name: "test-inv",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "Deployment",
},
},
isEqual: true,
},
// Two equal inventories with a namespace
{
inventory1: &Inventory{
Namespace: "test-namespace",
Name: "test-inv",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "Deployment",
},
},
inventory2: &Inventory{
Namespace: "test-namespace",
Name: "test-inv",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "Deployment",
},
},
isEqual: true,
},
// One inventory with a namespace, one without -- not equal.
{
inventory1: &Inventory{
Name: "test-inv",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "Deployment",
},
},
inventory2: &Inventory{
Namespace: "test-namespace",
Name: "test-inv",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "Deployment",
},
},
isEqual: false,
},
// One inventory with a Deployment, one with a ReplicaSet -- not equal.
{
inventory1: &Inventory{
Name: "test-inv",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "Deployment",
},
},
inventory2: &Inventory{
Name: "test-inv",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "ReplicaSet",
},
},
isEqual: false,
},
}
for _, test := range tests {
actual := test.inventory1.Equals(test.inventory2)
if test.isEqual && !actual {
t.Errorf("Expected inventories equal, but actual is not: (%s)/(%s)\n", test.inventory1, test.inventory2)
}
}
}
func TestParseInventory(t *testing.T) {
tests := []struct {
invStr string
inventory *Inventory
isError bool
}{
{
invStr: "_test-name_apps_ReplicaSet\t",
inventory: &Inventory{
Name: "test-name",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "ReplicaSet",
},
},
isError: false,
},
{
invStr: "test-namespace_test-name_apps_Deployment",
inventory: &Inventory{
Namespace: "test-namespace",
Name: "test-name",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "Deployment",
},
},
isError: false,
},
// Not enough fields -- error
{
invStr: "_test-name_apps",
inventory: &Inventory{},
isError: true,
},
}
for _, test := range tests {
actual, err := parseInventory(test.invStr)
if !test.isError {
if err != nil {
t.Errorf("Error parsing inventory when it should have worked.")
} else if !test.inventory.Equals(actual) {
t.Errorf("Expected inventory (%s) != parsed inventory (%s)\n", test.inventory, actual)
}
}
if test.isError && err == nil {
t.Errorf("Should have returned an error in parseInventory()")
}
}
}