change cluster schema (#2026)
* change cluster schema * change cluster schema
This commit is contained in:
264
vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/federate/util.go
generated
vendored
Normal file
264
vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/federate/util.go
generated
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
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
|
||||
|
||||
http://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 federate
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
utilyaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
versionhelper "k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/klog"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"sigs.k8s.io/kubefed/pkg/apis/core/typeconfig"
|
||||
ctlutil "sigs.k8s.io/kubefed/pkg/controller/util"
|
||||
"sigs.k8s.io/kubefed/pkg/kubefedctl/enable"
|
||||
"sigs.k8s.io/kubefed/pkg/kubefedctl/util"
|
||||
)
|
||||
|
||||
func RemoveUnwantedFields(resource *unstructured.Unstructured) error {
|
||||
unstructured.RemoveNestedField(resource.Object, "apiVersion")
|
||||
unstructured.RemoveNestedField(resource.Object, "kind")
|
||||
unstructured.RemoveNestedField(resource.Object, "status")
|
||||
|
||||
// All metadata fields save labels should be cleared. Other
|
||||
// metadata fields will be set by the system on creation or
|
||||
// subsequently by controllers.
|
||||
labels, _, err := unstructured.NestedMap(resource.Object, "metadata", "labels")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to retrieve metadata.labels")
|
||||
}
|
||||
unstructured.RemoveNestedField(resource.Object, "metadata")
|
||||
if len(labels) > 0 {
|
||||
err := unstructured.SetNestedMap(resource.Object, labels, "metadata", "labels")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to set metadata.labels")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetBasicMetaFields(resource *unstructured.Unstructured, apiResource metav1.APIResource, name, namespace, generateName string) {
|
||||
resource.SetKind(apiResource.Kind)
|
||||
gv := schema.GroupVersion{Group: apiResource.Group, Version: apiResource.Version}
|
||||
resource.SetAPIVersion(gv.String())
|
||||
resource.SetName(name)
|
||||
if generateName != "" {
|
||||
resource.SetGenerateName(generateName)
|
||||
}
|
||||
if apiResource.Namespaced {
|
||||
resource.SetNamespace(namespace)
|
||||
}
|
||||
}
|
||||
|
||||
func namespacedAPIResourceMap(config *rest.Config, skipAPIResourceNames []string) (map[string]metav1.APIResource, error) {
|
||||
apiResourceLists, err := enable.GetServerPreferredResources(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
apiResources := make(map[string]metav1.APIResource)
|
||||
for _, apiResourceList := range apiResourceLists {
|
||||
if len(apiResourceList.APIResources) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
gv, err := schema.ParseGroupVersion(apiResourceList.GroupVersion)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error parsing GroupVersion")
|
||||
}
|
||||
|
||||
group := gv.Group
|
||||
if apiResourceGroupMatchesSkipName(skipAPIResourceNames, group) {
|
||||
// A whole group is skipped by the user
|
||||
continue
|
||||
}
|
||||
|
||||
if group == "extensions" {
|
||||
// The strategy involved to choose a Group higher in order for k8s core
|
||||
// APIs is to consider "extensions" as the outdated group [This seems to
|
||||
// be true for all k8s APIResources, so far]. For example if "deployments"
|
||||
// exists in "extensions" and "apps"; "deployments.apps" will be chosen.
|
||||
// This doesn't apply to events but events are listed in
|
||||
// controllerCreatedAPIResourceNames and so are skipped always.
|
||||
|
||||
// Skipping this also assumes that "extensions" is not the only
|
||||
// group exposed for this resource on the API Server, which probably
|
||||
// is safe as "extensions" is deprecated.
|
||||
// TODO(irfanurrehman): Document this.
|
||||
continue
|
||||
}
|
||||
|
||||
for _, apiResource := range apiResourceList.APIResources {
|
||||
if !apiResource.Namespaced || util.IsFederatedAPIResource(apiResource.Kind, group) ||
|
||||
apiResourceMatchesSkipName(apiResource, skipAPIResourceNames, group) {
|
||||
continue
|
||||
}
|
||||
|
||||
// For all other resources (say CRDs) same kinds in different groups
|
||||
// are treated as individual types. If there happens to be an API Resource
|
||||
// which enables conversion and allows query of the same resource across
|
||||
// different groups, a specific group resource will have to be chosen by
|
||||
// the user using --skip-names to skip the not chosen one(s).
|
||||
// TODO(irfanurrehman): Document this.
|
||||
|
||||
// The individual apiResources do not have the group and version set
|
||||
apiResource.Group = group
|
||||
apiResource.Version = gv.Version
|
||||
groupQualifiedName := typeconfig.GroupQualifiedName(apiResource)
|
||||
if previousAPIResource, ok := apiResources[groupQualifiedName]; ok {
|
||||
if versionhelper.CompareKubeAwareVersionStrings(gv.Version, previousAPIResource.Version) <= 0 {
|
||||
// The newer version is not latest keep the previous.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
apiResources[groupQualifiedName] = apiResource
|
||||
}
|
||||
}
|
||||
|
||||
return apiResources, nil
|
||||
}
|
||||
|
||||
func apiResourceGroupMatchesSkipName(skipAPIResourceNames []string, group string) bool {
|
||||
for _, name := range skipAPIResourceNames {
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if name == group {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func apiResourceMatchesSkipName(apiResource metav1.APIResource, skipAPIResourceNames []string, group string) bool {
|
||||
names := append(controllerCreatedAPIResourceNames, skipAPIResourceNames...)
|
||||
for _, name := range names {
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if enable.NameMatchesResource(name, apiResource, group) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// resources stores a list of resources for an api type
|
||||
type resources struct {
|
||||
// resource type information
|
||||
apiResource metav1.APIResource
|
||||
// resource list
|
||||
resources []*unstructured.Unstructured
|
||||
}
|
||||
|
||||
func getResourcesInNamespace(config *rest.Config, namespace string, skipAPIResourceNames []string) ([]resources, error) {
|
||||
apiResources, err := namespacedAPIResourceMap(config, skipAPIResourceNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourcesInNamespace := []resources{}
|
||||
for _, apiResource := range apiResources {
|
||||
client, err := ctlutil.NewResourceClient(config, &apiResource)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Error creating client for %s", apiResource.Kind)
|
||||
}
|
||||
|
||||
resourceList, err := client.Resources(namespace).List(metav1.ListOptions{})
|
||||
if apierrors.IsNotFound(err) || resourceList == nil {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Error listing resources for %s", apiResource.Kind)
|
||||
}
|
||||
|
||||
// It would be a waste of cycles to iterate through empty slices while federating resource
|
||||
if len(resourceList.Items) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
targetResources := resources{apiResource: apiResource}
|
||||
for _, item := range resourceList.Items {
|
||||
resource := item
|
||||
errors := validation.IsDNS1123Subdomain(resource.GetName())
|
||||
if len(errors) == 0 {
|
||||
targetResources.resources = append(targetResources.resources, &resource)
|
||||
} else {
|
||||
klog.Warningf("Skipping resource %s of type %s because it does not conform to the DNS-1123 subdomain spec.", resource.GetName(), apiResource.Name)
|
||||
klog.Warningf("The following error(s) were reported during DNS-1123 validation: ")
|
||||
for _, err := range errors {
|
||||
klog.Warningf(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
resourcesInNamespace = append(resourcesInNamespace, targetResources)
|
||||
}
|
||||
|
||||
return resourcesInNamespace, nil
|
||||
}
|
||||
|
||||
// decodeUnstructuredFromFile reads a list of yamls into a slice of unstructured objects
|
||||
func DecodeUnstructuredFromFile(filename string) ([]*unstructured.Unstructured, error) {
|
||||
var f *os.File
|
||||
if filename == "-" {
|
||||
f = os.Stdin
|
||||
} else {
|
||||
var err error
|
||||
f, err = os.Open(filename)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var unstructuredList []*unstructured.Unstructured
|
||||
reader := utilyaml.NewYAMLReader(bufio.NewReader(f))
|
||||
for {
|
||||
unstructuedObj := &unstructured.Unstructured{}
|
||||
// Read one YAML document at a time, until io.EOF is returned
|
||||
buf, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
break
|
||||
}
|
||||
if err := yaml.Unmarshal(buf, unstructuedObj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unstructuredList = append(unstructuredList, unstructuedObj)
|
||||
}
|
||||
|
||||
return unstructuredList, nil
|
||||
}
|
||||
Reference in New Issue
Block a user