forked from rook/rook
145 lines
4.6 KiB
Go
145 lines
4.6 KiB
Go
/*
|
|
Package kit for Kubernetes operators
|
|
|
|
Copyright 2016 The Rook Authors. All rights reserved.
|
|
|
|
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.
|
|
|
|
Some of the code was modified from https://github.com/coreos/etcd-operator
|
|
which also has the apache 2.0 license.
|
|
*/
|
|
package kit
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/rook/rook/pkg/clusterd"
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
|
)
|
|
|
|
const (
|
|
// V1Alpha1 version for kubernetes resources
|
|
V1Alpha1 = "v1alpha1"
|
|
|
|
// V1Beta1 version for kubernetes resources
|
|
V1Beta1 = "v1beta1"
|
|
|
|
// V1 version for kubernetes resources
|
|
V1 = "v1"
|
|
)
|
|
|
|
// CustomResource is for creating a Kubernetes TPR/CRD
|
|
type CustomResource struct {
|
|
// Name of the custom resource
|
|
Name string
|
|
|
|
// Group the custom resource belongs to
|
|
Group string
|
|
|
|
// Version which should be defined in a const above
|
|
Version string
|
|
|
|
// Description that is human readable
|
|
Description string
|
|
}
|
|
|
|
// CreateCustomResources creates the given custom resources and waits for them to initialize
|
|
func CreateCustomResources(context clusterd.KubeContext, resources []CustomResource) error {
|
|
for _, resource := range resources {
|
|
if err := CreateCustomResource(context, resource); err != nil {
|
|
return fmt.Errorf("failed to init resource %s. %+v", resource.Name, err)
|
|
}
|
|
}
|
|
|
|
for _, resource := range resources {
|
|
if err := waitForCustomResourceInit(context, resource); err != nil {
|
|
return fmt.Errorf("failed to complete init %s. %+v", resource.Name, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CreateCustomResource creates a single custom resource, but does not wait for it to initialize
|
|
func CreateCustomResource(context clusterd.KubeContext, resource CustomResource) error {
|
|
logger.Infof("creating %s resource", resource.Name)
|
|
r := &v1beta1.ThirdPartyResource{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: fmt.Sprintf("%s.%s", resource.Name, resource.Group),
|
|
},
|
|
Versions: []v1beta1.APIVersion{
|
|
{Name: resource.Version},
|
|
},
|
|
Description: resource.Description,
|
|
}
|
|
_, err := context.Clientset.ExtensionsV1beta1().ThirdPartyResources().Create(r)
|
|
if err != nil {
|
|
if !errors.IsAlreadyExists(err) {
|
|
return fmt.Errorf("failed to create %s resource. %+v", resource.Name, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func waitForCustomResourceInit(context clusterd.KubeContext, scheme CustomResource) error {
|
|
restcli := context.Clientset.CoreV1().RESTClient()
|
|
uri := resourceURI(resource, "")
|
|
return Retry(context, func() (bool, error) {
|
|
_, err := restcli.Get().RequestURI(uri).DoRaw()
|
|
if err != nil {
|
|
logger.Infof("did not yet find resource %s at %s. %+v", resource.Name, uri, err)
|
|
if errors.IsNotFound(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
})
|
|
}
|
|
|
|
func watchResource(context clusterd.KubeContext, scheme CustomResource, namespace, resourceVersion string) (*http.Response, error) {
|
|
uri := fmt.Sprintf("%s/%s?watch=true&resourceVersion=%s", context.MasterHost, resourceURI(scheme, namespace), resourceVersion)
|
|
logger.Debugf("watching resource: %s", uri)
|
|
return context.KubeHttpCli.Get(uri)
|
|
}
|
|
|
|
func resourceURI(resource CustomResource, namespace string) string {
|
|
if namespace == "" {
|
|
// creates a uri that is for retrieving or watching a resource in all namespaces. For example:
|
|
// /apis/rook.io/v1alpha1/clusters
|
|
return fmt.Sprintf("apis/%s/%s/%ss", resource.Group, resource.Version, resource.Name)
|
|
}
|
|
|
|
// create a uri that is for a specific namespace
|
|
// /apis/rook.io/v1alpha1/namespaces/rook/pools
|
|
return fmt.Sprintf("apis/%s/%s/namespaces/%s/%ss", resource.Group, resource.Version, namespace, resource.Name)
|
|
}
|
|
|
|
// GetRawListNamespaced retrieves a list custom resources of the given type in a specific namespace
|
|
func GetRawListNamespaced(clientset kubernetes.Interface, resource CustomResource, namespace string) ([]byte, error) {
|
|
restcli := clientset.CoreV1().RESTClient()
|
|
uri := resourceURI(resource, namespace)
|
|
return restcli.Get().RequestURI(uri).DoRaw()
|
|
}
|
|
|
|
// GetRawList retrieves a list of custom resources of the given type across all namespaces
|
|
func GetRawList(clientset kubernetes.Interface, resource CustomResource) ([]byte, error) {
|
|
return GetRawListNamespaced(clientset, resource, "")
|
|
}
|