2017-03-02 12:26:06 +01:00
|
|
|
package kubernetes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2017-03-21 22:04:54 +01:00
|
|
|
"net/url"
|
2017-03-16 15:51:01 +01:00
|
|
|
"strings"
|
2017-03-02 12:26:06 +01:00
|
|
|
|
2017-03-30 10:24:40 +02:00
|
|
|
"encoding/base64"
|
2017-03-21 22:01:18 +01:00
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
2017-05-26 08:50:26 +02:00
|
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2017-03-02 12:26:06 +01:00
|
|
|
api "k8s.io/kubernetes/pkg/api/v1"
|
|
|
|
)
|
|
|
|
|
2017-03-16 15:51:01 +01:00
|
|
|
func idParts(id string) (string, string) {
|
|
|
|
parts := strings.Split(id, "/")
|
|
|
|
return parts[0], parts[1]
|
|
|
|
}
|
|
|
|
|
2017-05-26 08:50:26 +02:00
|
|
|
func buildId(meta metav1.ObjectMeta) string {
|
2017-03-16 15:51:01 +01:00
|
|
|
return meta.Namespace + "/" + meta.Name
|
|
|
|
}
|
|
|
|
|
2017-05-26 08:50:26 +02:00
|
|
|
func expandMetadata(in []interface{}) metav1.ObjectMeta {
|
|
|
|
meta := metav1.ObjectMeta{}
|
2017-03-02 12:26:06 +01:00
|
|
|
if len(in) < 1 {
|
|
|
|
return meta
|
|
|
|
}
|
|
|
|
m := in[0].(map[string]interface{})
|
|
|
|
|
|
|
|
meta.Annotations = expandStringMap(m["annotations"].(map[string]interface{}))
|
|
|
|
meta.Labels = expandStringMap(m["labels"].(map[string]interface{}))
|
|
|
|
|
|
|
|
if v, ok := m["generate_name"]; ok {
|
|
|
|
meta.GenerateName = v.(string)
|
|
|
|
}
|
|
|
|
if v, ok := m["name"]; ok {
|
|
|
|
meta.Name = v.(string)
|
|
|
|
}
|
|
|
|
if v, ok := m["namespace"]; ok {
|
|
|
|
meta.Namespace = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
return meta
|
|
|
|
}
|
|
|
|
|
2017-03-21 22:01:18 +01:00
|
|
|
func patchMetadata(keyPrefix, pathPrefix string, d *schema.ResourceData) PatchOperations {
|
|
|
|
ops := make([]PatchOperation, 0, 0)
|
|
|
|
if d.HasChange(keyPrefix + "annotations") {
|
|
|
|
oldV, newV := d.GetChange(keyPrefix + "annotations")
|
|
|
|
diffOps := diffStringMap(pathPrefix+"annotations", oldV.(map[string]interface{}), newV.(map[string]interface{}))
|
|
|
|
ops = append(ops, diffOps...)
|
|
|
|
}
|
|
|
|
if d.HasChange(keyPrefix + "labels") {
|
|
|
|
oldV, newV := d.GetChange(keyPrefix + "labels")
|
|
|
|
diffOps := diffStringMap(pathPrefix+"labels", oldV.(map[string]interface{}), newV.(map[string]interface{}))
|
|
|
|
ops = append(ops, diffOps...)
|
|
|
|
}
|
|
|
|
return ops
|
|
|
|
}
|
|
|
|
|
2017-03-02 12:26:06 +01:00
|
|
|
func expandStringMap(m map[string]interface{}) map[string]string {
|
|
|
|
result := make(map[string]string)
|
|
|
|
for k, v := range m {
|
|
|
|
result[k] = v.(string)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2017-04-02 08:08:55 +02:00
|
|
|
func expandStringSlice(s []interface{}) []string {
|
|
|
|
result := make([]string, len(s), len(s))
|
|
|
|
for k, v := range s {
|
|
|
|
result[k] = v.(string)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2017-05-26 08:50:26 +02:00
|
|
|
func flattenMetadata(meta metav1.ObjectMeta) []map[string]interface{} {
|
2017-03-02 12:26:06 +01:00
|
|
|
m := make(map[string]interface{})
|
2017-06-02 15:15:26 +02:00
|
|
|
m["annotations"] = removeInternalKeys(meta.Annotations)
|
2017-04-02 08:08:16 +02:00
|
|
|
if meta.GenerateName != "" {
|
|
|
|
m["generate_name"] = meta.GenerateName
|
|
|
|
}
|
2017-06-02 15:15:26 +02:00
|
|
|
m["labels"] = removeInternalKeys(meta.Labels)
|
2017-03-02 12:26:06 +01:00
|
|
|
m["name"] = meta.Name
|
|
|
|
m["resource_version"] = meta.ResourceVersion
|
|
|
|
m["self_link"] = meta.SelfLink
|
|
|
|
m["uid"] = fmt.Sprintf("%v", meta.UID)
|
|
|
|
m["generation"] = meta.Generation
|
|
|
|
|
|
|
|
if meta.Namespace != "" {
|
|
|
|
m["namespace"] = meta.Namespace
|
|
|
|
}
|
|
|
|
|
|
|
|
return []map[string]interface{}{m}
|
|
|
|
}
|
2017-03-21 22:04:54 +01:00
|
|
|
|
2017-06-02 15:15:26 +02:00
|
|
|
func removeInternalKeys(m map[string]string) map[string]string {
|
2017-03-21 22:04:54 +01:00
|
|
|
for k, _ := range m {
|
2017-06-02 15:15:26 +02:00
|
|
|
if isInternalKey(k) {
|
2017-03-21 22:04:54 +01:00
|
|
|
delete(m, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2017-06-02 15:15:26 +02:00
|
|
|
func isInternalKey(annotationKey string) bool {
|
2017-03-21 22:04:54 +01:00
|
|
|
u, err := url.Parse("//" + annotationKey)
|
|
|
|
if err == nil && strings.HasSuffix(u.Hostname(), "kubernetes.io") {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
2017-03-30 10:24:40 +02:00
|
|
|
|
|
|
|
func byteMapToStringMap(m map[string][]byte) map[string]string {
|
|
|
|
result := make(map[string]string)
|
|
|
|
for k, v := range m {
|
|
|
|
result[k] = string(v)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2017-04-02 08:08:55 +02:00
|
|
|
func ptrToString(s string) *string {
|
|
|
|
return &s
|
|
|
|
}
|
|
|
|
|
|
|
|
func ptrToInt(i int) *int {
|
|
|
|
return &i
|
|
|
|
}
|
|
|
|
|
|
|
|
func ptrToBool(b bool) *bool {
|
|
|
|
return &b
|
|
|
|
}
|
|
|
|
|
|
|
|
func ptrToInt32(i int32) *int32 {
|
|
|
|
return &i
|
|
|
|
}
|
|
|
|
|
|
|
|
func sliceOfString(slice []interface{}) []string {
|
|
|
|
result := make([]string, len(slice), len(slice))
|
|
|
|
for i, s := range slice {
|
|
|
|
result[i] = s.(string)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2017-03-30 10:24:40 +02:00
|
|
|
func base64EncodeStringMap(m map[string]interface{}) map[string]interface{} {
|
|
|
|
result := make(map[string]interface{})
|
|
|
|
for k, v := range m {
|
|
|
|
value := v.(string)
|
|
|
|
result[k] = (base64.StdEncoding.EncodeToString([]byte(value)))
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
2017-04-02 08:08:55 +02:00
|
|
|
|
|
|
|
func flattenResourceList(l api.ResourceList) map[string]string {
|
|
|
|
m := make(map[string]string)
|
|
|
|
for k, v := range l {
|
|
|
|
m[string(k)] = v.String()
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func expandMapToResourceList(m map[string]interface{}) (api.ResourceList, error) {
|
|
|
|
out := make(map[api.ResourceName]resource.Quantity)
|
2017-05-08 13:43:24 +02:00
|
|
|
for stringKey, origValue := range m {
|
2017-04-02 08:08:55 +02:00
|
|
|
key := api.ResourceName(stringKey)
|
2017-05-08 13:43:24 +02:00
|
|
|
var value resource.Quantity
|
|
|
|
|
|
|
|
if v, ok := origValue.(int); ok {
|
|
|
|
q := resource.NewQuantity(int64(v), resource.DecimalExponent)
|
|
|
|
value = *q
|
|
|
|
} else if v, ok := origValue.(string); ok {
|
|
|
|
var err error
|
|
|
|
value, err = resource.ParseQuantity(v)
|
|
|
|
if err != nil {
|
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return out, fmt.Errorf("Unexpected value type: %#v", origValue)
|
2017-04-02 08:08:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
out[key] = value
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func flattenPersistentVolumeAccessModes(in []api.PersistentVolumeAccessMode) *schema.Set {
|
|
|
|
var out = make([]interface{}, len(in), len(in))
|
|
|
|
for i, v := range in {
|
|
|
|
out[i] = string(v)
|
|
|
|
}
|
|
|
|
return schema.NewSet(schema.HashString, out)
|
|
|
|
}
|
|
|
|
|
|
|
|
func expandPersistentVolumeAccessModes(s []interface{}) []api.PersistentVolumeAccessMode {
|
|
|
|
out := make([]api.PersistentVolumeAccessMode, len(s), len(s))
|
|
|
|
for i, v := range s {
|
|
|
|
out[i] = api.PersistentVolumeAccessMode(v.(string))
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2017-05-08 13:43:24 +02:00
|
|
|
func flattenResourceQuotaSpec(in api.ResourceQuotaSpec) []interface{} {
|
|
|
|
out := make([]interface{}, 1)
|
|
|
|
|
|
|
|
m := make(map[string]interface{}, 0)
|
|
|
|
m["hard"] = flattenResourceList(in.Hard)
|
|
|
|
m["scopes"] = flattenResourceQuotaScopes(in.Scopes)
|
|
|
|
|
|
|
|
out[0] = m
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
func expandResourceQuotaSpec(s []interface{}) (api.ResourceQuotaSpec, error) {
|
|
|
|
out := api.ResourceQuotaSpec{}
|
|
|
|
if len(s) < 1 {
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
m := s[0].(map[string]interface{})
|
|
|
|
|
|
|
|
if v, ok := m["hard"]; ok {
|
|
|
|
list, err := expandMapToResourceList(v.(map[string]interface{}))
|
|
|
|
if err != nil {
|
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
out.Hard = list
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := m["scopes"]; ok {
|
|
|
|
out.Scopes = expandResourceQuotaScopes(v.(*schema.Set).List())
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func flattenResourceQuotaScopes(in []api.ResourceQuotaScope) *schema.Set {
|
|
|
|
out := make([]string, len(in), len(in))
|
|
|
|
for i, scope := range in {
|
|
|
|
out[i] = string(scope)
|
|
|
|
}
|
|
|
|
return newStringSet(schema.HashString, out)
|
|
|
|
}
|
|
|
|
|
|
|
|
func expandResourceQuotaScopes(s []interface{}) []api.ResourceQuotaScope {
|
|
|
|
out := make([]api.ResourceQuotaScope, len(s), len(s))
|
|
|
|
for i, scope := range s {
|
|
|
|
out[i] = api.ResourceQuotaScope(scope.(string))
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2017-04-02 08:08:55 +02:00
|
|
|
func newStringSet(f schema.SchemaSetFunc, in []string) *schema.Set {
|
|
|
|
var out = make([]interface{}, len(in), len(in))
|
|
|
|
for i, v := range in {
|
|
|
|
out[i] = v
|
|
|
|
}
|
|
|
|
return schema.NewSet(f, out)
|
|
|
|
}
|
2017-05-08 13:43:24 +02:00
|
|
|
|
|
|
|
func resourceListEquals(x, y api.ResourceList) bool {
|
|
|
|
for k, v := range x {
|
|
|
|
yValue, ok := y[k]
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if v.Cmp(yValue) != 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for k, v := range y {
|
|
|
|
xValue, ok := x[k]
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if v.Cmp(xValue) != 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
2017-05-09 05:07:17 +02:00
|
|
|
|
|
|
|
func expandLimitRangeSpec(s []interface{}, isNew bool) (api.LimitRangeSpec, error) {
|
|
|
|
out := api.LimitRangeSpec{}
|
|
|
|
if len(s) < 1 || s[0] == nil {
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
m := s[0].(map[string]interface{})
|
|
|
|
|
|
|
|
if limits, ok := m["limit"].([]interface{}); ok {
|
|
|
|
newLimits := make([]api.LimitRangeItem, len(limits), len(limits))
|
|
|
|
|
|
|
|
for i, l := range limits {
|
|
|
|
lrItem := api.LimitRangeItem{}
|
|
|
|
limit := l.(map[string]interface{})
|
|
|
|
|
|
|
|
if v, ok := limit["type"]; ok {
|
|
|
|
lrItem.Type = api.LimitType(v.(string))
|
|
|
|
}
|
|
|
|
|
|
|
|
// defaultRequest is forbidden for Pod limits, even though it's set & returned by API
|
|
|
|
// this is how we avoid sending it back
|
|
|
|
if v, ok := limit["default_request"]; ok {
|
|
|
|
drm := v.(map[string]interface{})
|
|
|
|
if lrItem.Type == api.LimitTypePod && len(drm) > 0 {
|
|
|
|
if isNew {
|
|
|
|
return out, fmt.Errorf("limit.%d.default_request cannot be set for Pod limit", i)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
el, err := expandMapToResourceList(drm)
|
|
|
|
if err != nil {
|
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
lrItem.DefaultRequest = el
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := limit["default"]; ok {
|
|
|
|
el, err := expandMapToResourceList(v.(map[string]interface{}))
|
|
|
|
if err != nil {
|
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
lrItem.Default = el
|
|
|
|
}
|
|
|
|
if v, ok := limit["max"]; ok {
|
|
|
|
el, err := expandMapToResourceList(v.(map[string]interface{}))
|
|
|
|
if err != nil {
|
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
lrItem.Max = el
|
|
|
|
}
|
|
|
|
if v, ok := limit["max_limit_request_ratio"]; ok {
|
|
|
|
el, err := expandMapToResourceList(v.(map[string]interface{}))
|
|
|
|
if err != nil {
|
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
lrItem.MaxLimitRequestRatio = el
|
|
|
|
}
|
|
|
|
if v, ok := limit["min"]; ok {
|
|
|
|
el, err := expandMapToResourceList(v.(map[string]interface{}))
|
|
|
|
if err != nil {
|
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
lrItem.Min = el
|
|
|
|
}
|
|
|
|
|
|
|
|
newLimits[i] = lrItem
|
|
|
|
}
|
|
|
|
|
|
|
|
out.Limits = newLimits
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func flattenLimitRangeSpec(in api.LimitRangeSpec) []interface{} {
|
|
|
|
out := make([]interface{}, 1)
|
|
|
|
limits := make([]interface{}, len(in.Limits), len(in.Limits))
|
|
|
|
|
|
|
|
for i, l := range in.Limits {
|
|
|
|
m := make(map[string]interface{}, 0)
|
|
|
|
m["default"] = flattenResourceList(l.Default)
|
|
|
|
m["default_request"] = flattenResourceList(l.DefaultRequest)
|
|
|
|
m["max"] = flattenResourceList(l.Max)
|
|
|
|
m["max_limit_request_ratio"] = flattenResourceList(l.MaxLimitRequestRatio)
|
|
|
|
m["min"] = flattenResourceList(l.Min)
|
|
|
|
m["type"] = string(l.Type)
|
|
|
|
|
|
|
|
limits[i] = m
|
|
|
|
}
|
|
|
|
out[0] = map[string]interface{}{
|
|
|
|
"limit": limits,
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|