forked from rook/operator-kit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresource.go
161 lines (131 loc) · 5.16 KB
/
resource.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*
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 operatorkit
import (
"fmt"
"net/http"
"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"
)
// KubeContext provides the context for connecting to Kubernetes APIs
type KubeContext struct {
// Clientset is a connection to the core kubernetes API
Clientset kubernetes.Interface
// RetryDelay is the number of seconds to delay between retrying of kubernetes API calls.
// Only used by the Retry function.
RetryDelay int
// MaxRetries is the number of times that an operation will be attempted by the Retry function.
MaxRetries int
// The host where the Kubernetes master is found.
MasterHost string
// An http connection to the Kubernetes API
KubeHTTPCli *http.Client
}
// 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 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 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 KubeContext, resource 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 KubeContext, resource CustomResource, namespace, resourceVersion string) (*http.Response, error) {
uri := fmt.Sprintf("%s/%s?watch=true&resourceVersion=%s", context.MasterHost, resourceURI(resource, 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, "")
}