cli-utils/pkg/inventory/inventorycm.go

110 lines
3.2 KiB
Go

// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//
// Introduces the InventoryConfigMap struct which implements
// the Inventory interface. The InventoryConfigMap wraps a
// ConfigMap resource which stores the set of inventory
// (object metadata).
package inventory
import (
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/cli-runtime/pkg/resource"
"sigs.k8s.io/cli-utils/pkg/object"
)
// WrapInventoryObj takes a passed ConfigMap (as a resource.Info),
// wraps it with the InventoryConfigMap and upcasts the wrapper as
// an the Inventory interface.
func WrapInventoryObj(info *resource.Info) Inventory {
return &InventoryConfigMap{inv: info}
}
// InventoryConfigMap wraps a ConfigMap resource and implements
// the Inventory interface. This wrapper loads and stores the
// object metadata (inventory) to and from the wrapped ConfigMap.
type InventoryConfigMap struct {
inv *resource.Info
objMetas []object.ObjMetadata
}
// Load is an Inventory interface function returning the set of
// object metadata from the wrapped ConfigMap, or an error.
func (icm *InventoryConfigMap) Load() ([]object.ObjMetadata, error) {
objs := []object.ObjMetadata{}
inventoryObj, ok := icm.inv.Object.(*unstructured.Unstructured)
if !ok {
err := fmt.Errorf("inventory object is not an Unstructured: %#v", inventoryObj)
return objs, err
}
objMap, exists, err := unstructured.NestedStringMap(inventoryObj.Object, "data")
if err != nil {
err := fmt.Errorf("error retrieving object metadata from inventory object")
return objs, err
}
if exists {
for objStr := range objMap {
obj, err := object.ParseObjMetadata(objStr)
if err != nil {
return objs, err
}
objs = append(objs, obj)
}
}
return objs, nil
}
// Store is an Inventory interface function implemented to store
// the object metadata in the wrapped ConfigMap. Actual storing
// happens in "GetObject".
func (icm *InventoryConfigMap) Store(objMetas []object.ObjMetadata) error {
icm.objMetas = objMetas
return nil
}
// GetObject returns the wrapped object (ConfigMap) as a resource.Info
// or an error if one occurs.
func (icm *InventoryConfigMap) GetObject() (*resource.Info, error) {
// Verify the ConfigMap is in Unstructured format.
obj := icm.inv.Object
if obj == nil {
return nil, fmt.Errorf("inventory info has nil Object")
}
iot, ok := obj.(*unstructured.Unstructured)
if !ok {
return nil, fmt.Errorf(
"inventory ConfigMap is not in Unstructured format: %s",
icm.inv.Source)
}
// Create the objMap of all the resources, and compute the hash.
objMap := buildObjMap(icm.objMetas)
// Create the inventory object by copying the template.
invCopy := iot.DeepCopy()
// Adds the inventory map to the ConfigMap "data" section.
err := unstructured.SetNestedStringMap(invCopy.UnstructuredContent(),
objMap, "data")
if err != nil {
return nil, err
}
return &resource.Info{
Client: icm.inv.Client,
Mapping: icm.inv.Mapping,
Source: "generated",
Name: invCopy.GetName(),
Namespace: invCopy.GetNamespace(),
Object: invCopy,
}, nil
}
func buildObjMap(objMetas []object.ObjMetadata) map[string]string {
objMap := map[string]string{}
for _, objMetadata := range objMetas {
objMap[objMetadata.String()] = ""
}
return objMap
}