pkg/find/finder.go (99 lines of code) (raw):
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package find
import (
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
bundle "github.com/GoogleCloudPlatform/k8s-cluster-bundle/pkg/apis/bundle/v1alpha1"
"github.com/GoogleCloudPlatform/k8s-cluster-bundle/pkg/core"
)
// ComponentFinder is a wrapper which allows for efficient searching through
// component data. The data is intended to be readonly; if modifications are
// made to the data, subsequent lookups will fail.
type ComponentFinder struct {
nameCompLookup map[string][]*bundle.Component
keyCompLookup map[bundle.ComponentReference]*bundle.Component
data []*bundle.Component
}
// NewComponentFinder creates a new ComponentFinder or returns an error.
func NewComponentFinder(data []*bundle.Component) *ComponentFinder {
nlup := make(map[string][]*bundle.Component)
klup := make(map[bundle.ComponentReference]*bundle.Component)
for _, comp := range data {
name := comp.Spec.ComponentName
klup[comp.ComponentReference()] = comp
if list := nlup[name]; list == nil {
nlup[name] = []*bundle.Component{comp}
} else {
nlup[name] = append(nlup[name], comp)
}
}
return &ComponentFinder{
nameCompLookup: nlup,
keyCompLookup: klup,
data: data,
}
}
// Component returns the component package that matches a reference,
// returning nil if no match is found.
func (f *ComponentFinder) Component(ref bundle.ComponentReference) *bundle.Component {
return f.keyCompLookup[ref]
}
// ComponentVersions returns the all the component versions for a given
// component name. The references are not sorted.
func (f *ComponentFinder) ComponentVersions(name string) []bundle.ComponentReference {
comps := f.nameCompLookup[name]
var refs []bundle.ComponentReference
for _, c := range comps {
refs = append(refs, c.ComponentReference())
}
return refs
}
// AllComponents return all the components known by the finder.
func (f *ComponentFinder) AllComponents() []*bundle.Component {
var out []*bundle.Component
for _, c := range f.keyCompLookup {
out = append(out, c)
}
return out
}
// UniqueComponentFromName returns the single component package that matches a
// string-name. If no component is found, nil is returned. If there are two
// components that match the name, the method returns an error.
func (f *ComponentFinder) UniqueComponentFromName(name string) (*bundle.Component, error) {
comps := f.ComponentVersions(name)
if len(comps) == 0 {
return nil, nil
} else if len(comps) > 1 {
return nil, fmt.Errorf("duplicate component found for name %q", name)
}
return f.Component(comps[0]), nil
}
// Objects returns Component's Cluster objects (given some object
// ref) or nil.
func (f *ComponentFinder) Objects(cref bundle.ComponentReference, ref core.ObjectRef) []*unstructured.Unstructured {
comp := f.Component(cref)
if comp == nil {
return nil
}
return NewObjectFinder(comp).Objects(ref)
}
// ObjectsFromUniqueComponent gets the objects for a component, which
// has the same behavior as Objects, except that the component name is
// assumed to be unique (and so panics if that assumption does not hold).
func (f *ComponentFinder) ObjectsFromUniqueComponent(name string, ref core.ObjectRef) ([]*unstructured.Unstructured, error) {
comp, err := f.UniqueComponentFromName(name)
if err != nil {
return nil, err
}
if comp == nil {
return nil, nil
}
return NewObjectFinder(comp).Objects(ref), nil
}
// ObjectFinder finds objects within components
type ObjectFinder struct {
component *bundle.Component
}
// NewObjectFinder returns an ObjectFinder instance.
func NewObjectFinder(component *bundle.Component) *ObjectFinder {
return &ObjectFinder{component}
}
// Objects finds cluster objects matching a certain ObjectRef key. If
// the ObjectRef is partially filled out, then only those fields will be used
// for searching and the partial matches will be returned.
func (c *ObjectFinder) Objects(ref core.ObjectRef) []*unstructured.Unstructured {
var out []*unstructured.Unstructured
for _, o := range c.component.Spec.Objects {
var key core.ObjectRef
if ref.Name != "" {
// Doing a search based on name
key.Name = o.GetName()
}
if ref.APIVersion != "" {
// Doing a search based on API version
key.APIVersion = o.GetAPIVersion()
}
if ref.Kind != "" {
// Doing a search based on kind
key.Kind = o.GetKind()
}
if key == ref {
out = append(out, o)
}
}
return out
}