Files
kubesphere/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/federate/util.go
zryfish 5a3eb651f3 change cluster schema (#2026)
* change cluster schema

* change cluster schema
2020-04-27 17:34:02 +08:00

265 lines
8.2 KiB
Go

/*
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
}