add service mesh controller

add service mesh metrics

remove unused circle yaml

fix travis misconfiguration

fix travis misconfiguration

fix travis misconfiguration
This commit is contained in:
jeff
2019-03-08 18:22:30 +08:00
committed by Jeff
parent 858facd4b2
commit 4ac20ffc2b
1709 changed files with 344390 additions and 60749 deletions

View File

@@ -0,0 +1,189 @@
/*
Copyright 2018 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 internal
import (
"bytes"
"context"
"errors"
"sort"
"github.com/ghodss/yaml"
"github.com/spf13/afero"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var decoder = scheme.Codecs.UniversalDeserializer()
// NewManifestClient constructs a new manifestClient.
func NewManifestClient(file string) client.Client {
return &manifestClient{
ManifestFile: file,
fs: afero.NewOsFs(),
}
}
// manifestClient reads from and writes to the file specified by ManifestFile.
type manifestClient struct {
ManifestFile string
objects map[schema.GroupVersionKind][]byte
fs afero.Fs
}
var _ client.Client = &manifestClient{}
func (c *manifestClient) index() error {
c.objects = map[schema.GroupVersionKind][]byte{}
_, err := c.fs.Stat(c.ManifestFile)
if err != nil {
return nil
}
b, err := afero.ReadFile(c.fs, c.ManifestFile)
if err != nil {
return err
}
objs := bytes.Split(b, []byte("---\n"))
for _, objectB := range objs {
objB := bytes.TrimSpace(objectB)
if len(objB) == 0 {
continue
}
_, gvk, err := decoder.Decode(objB, nil, nil)
if err != nil {
return err
}
c.objects[*gvk] = objectB
}
return nil
}
// Get read from the target file.
func (c *manifestClient) Get(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
if obj == nil {
return errors.New("obj should not be nil")
}
err := c.index()
if err != nil {
return err
}
gvk := obj.GetObjectKind().GroupVersionKind()
objectB, found := c.objects[gvk]
if !found {
return apierrors.NewNotFound(schema.GroupResource{}, key.Name)
}
_, _, err = decoder.Decode(objectB, nil, obj)
return err
}
// List does nothing, it should not be invoked.
func (c *manifestClient) List(ctx context.Context, opts *client.ListOptions, list runtime.Object) error {
return errors.New("method List is not implemented")
}
// Create creates an object and write it to the target file with other objects if any.
// If the object needs to be written already exists, it will error out.
func (c *manifestClient) Create(ctx context.Context, obj runtime.Object) error {
if obj == nil {
return errors.New("obj should not be nil")
}
err := c.index()
if err != nil {
return err
}
gvk := obj.GetObjectKind().GroupVersionKind()
_, found := c.objects[gvk]
if found {
accessor, err := meta.Accessor(obj)
if err != nil {
return err
}
return apierrors.NewAlreadyExists(schema.GroupResource{}, accessor.GetName())
}
b, err := yaml.Marshal(obj)
if err != nil {
return err
}
c.objects[gvk] = b
return c.writeObjects()
}
// Delete does nothing, it should not be invoked.
func (c *manifestClient) Delete(ctx context.Context, obj runtime.Object, opts ...client.DeleteOptionFunc) error {
return errors.New("method Delete is not implemented")
}
// Update replace the object if it already exists on the target file.
// Otherwise, it creates the object.
func (c *manifestClient) Update(ctx context.Context, obj runtime.Object) error {
if obj == nil {
return errors.New("obj should not be nil")
}
err := c.index()
if err != nil {
return err
}
gvk := obj.GetObjectKind().GroupVersionKind()
m, err := yaml.Marshal(obj)
if err != nil {
return err
}
c.objects[gvk] = m
return c.writeObjects()
}
// writeObjects writes objects to the target file in yaml format separated by `---`.
func (c *manifestClient) writeObjects() error {
needSeparator := false
buf := bytes.NewBuffer(nil)
var gvks []schema.GroupVersionKind
for gvk := range c.objects {
gvks = append(gvks, gvk)
}
sort.Slice(gvks, func(i, j int) bool {
return gvks[i].String() < gvks[j].String()
})
for _, gvk := range gvks {
if needSeparator {
buf.WriteString("---\n")
}
needSeparator = true
_, err := buf.Write(c.objects[gvk])
if err != nil {
return err
}
}
return afero.WriteFile(c.fs, c.ManifestFile, buf.Bytes(), 0666)
}
// Status returns a nil client.StatusWriter.
func (c *manifestClient) Status() client.StatusWriter {
return nil
}

View File

@@ -0,0 +1,70 @@
/*
Copyright 2018 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 internal
import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
)
// Manager is a dummy manager that does nothing.
type Manager struct{}
var _ manager.Manager = &Manager{}
// Add will set reqeusted dependencies on the component, and cause the component to be
// started when Start is called. Add will inject any dependencies for which the argument
// implements the inject interface - e.g. inject.Client
func (m *Manager) Add(manager.Runnable) error { return nil }
// SetFields will set any dependencies on an object for which the object has implemented the inject
// interface - e.g. inject.Client.
func (m *Manager) SetFields(interface{}) error { return nil }
// Start starts all registered Controllers and blocks until the Stop channel is closed.
// Returns an error if there is an error starting any controller.
func (m *Manager) Start(<-chan struct{}) error { return nil }
// GetConfig returns an initialized Config
func (m *Manager) GetConfig() *rest.Config { return nil }
// GetScheme returns and initialized Scheme
func (m *Manager) GetScheme() *runtime.Scheme { return nil }
// GetAdmissionDecoder returns the runtime.Decoder based on the scheme.
func (m *Manager) GetAdmissionDecoder() types.Decoder { return nil }
// GetClient returns a client configured with the Config
func (m *Manager) GetClient() client.Client { return nil }
// GetFieldIndexer returns a client.FieldIndexer configured with the client
func (m *Manager) GetFieldIndexer() client.FieldIndexer { return nil }
// GetCache returns a cache.Cache
func (m *Manager) GetCache() cache.Cache { return nil }
// GetRecorder returns a new EventRecorder for the provided name
func (m *Manager) GetRecorder(name string) record.EventRecorder { return nil }
// GetRESTMapper returns a RESTMapper
func (m *Manager) GetRESTMapper() meta.RESTMapper { return nil }

View File

@@ -0,0 +1,163 @@
/*
Copyright 2018 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 webhook
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"text/template"
"github.com/ghodss/yaml"
"sigs.k8s.io/controller-runtime/pkg/webhook"
generateinteral "sigs.k8s.io/controller-tools/pkg/internal/general"
"sigs.k8s.io/controller-tools/pkg/webhook/internal"
)
// ManifestOptions represent options for generating the webhook manifests.
type ManifestOptions struct {
InputDir string
OutputDir string
PatchOutputDir string
webhooks []webhook.Webhook
svrOps *webhook.ServerOptions
svr *webhook.Server
}
// SetDefaults sets up the default options for RBAC Manifest generator.
func (o *ManifestOptions) SetDefaults() {
o.InputDir = filepath.Join(".", "pkg", "webhook")
o.OutputDir = filepath.Join(".", "config", "webhook")
o.PatchOutputDir = filepath.Join(".", "config", "default")
}
// Validate validates the input options.
func (o *ManifestOptions) Validate() error {
if _, err := os.Stat(o.InputDir); err != nil {
return fmt.Errorf("invalid input directory '%s' %v", o.InputDir, err)
}
return nil
}
// Generate generates RBAC manifests by parsing the RBAC annotations in Go source
// files specified in the input directory.
func Generate(o *ManifestOptions) error {
if err := o.Validate(); err != nil {
return err
}
_, err := os.Stat(o.OutputDir)
if os.IsNotExist(err) {
err = os.MkdirAll(o.OutputDir, 0766)
if err != nil {
return err
}
} else if err != nil {
return err
}
o.webhooks = []webhook.Webhook{}
o.svrOps = &webhook.ServerOptions{
Client: internal.NewManifestClient(path.Join(o.OutputDir, "webhook.yaml")),
}
err = generateinteral.ParseDir(o.InputDir, o.parseAnnotation)
if err != nil {
return fmt.Errorf("failed to parse the input dir: %v", err)
}
o.svr, err = webhook.NewServer("generator", &internal.Manager{}, *o.svrOps)
if err != nil {
return err
}
err = o.svr.Register(o.webhooks...)
if err != nil {
return fmt.Errorf("failed to process the input before generating: %v", err)
}
err = o.svr.InstallWebhookManifests()
if err != nil {
return err
}
return o.labelPatch()
}
func (o *ManifestOptions) labelPatch() error {
var kustomizeLabelPatch = `apiVersion: apps/v1
kind: StatefulSet
metadata:
name: controller-manager
spec:
template:
metadata:
{{- with .Labels }}
labels:
{{ toYaml . | indent 8 }}
{{- end }}
`
type KustomizeLabelPatch struct {
Labels map[string]string
}
p := KustomizeLabelPatch{Labels: o.svrOps.Service.Selectors}
funcMap := template.FuncMap{
"toYaml": toYAML,
"indent": indent,
}
temp, err := template.New("kustomizeLabelPatch").Funcs(funcMap).Parse(kustomizeLabelPatch)
if err != nil {
return err
}
buf := bytes.NewBuffer(nil)
if err := temp.Execute(buf, p); err != nil {
return err
}
return ioutil.WriteFile(path.Join(o.PatchOutputDir, "manager_label_patch.yaml"), buf.Bytes(), 0644)
}
func toYAML(m map[string]string) (string, error) {
d, err := yaml.Marshal(m)
return string(d), err
}
func indent(n int, s string) (string, error) {
buf := bytes.NewBuffer(nil)
for _, elem := range strings.Split(s, "\n") {
for i := 0; i < n; i++ {
_, err := buf.WriteRune(' ')
if err != nil {
return "", err
}
}
_, err := buf.WriteString(elem)
if err != nil {
return "", err
}
_, err = buf.WriteRune('\n')
if err != nil {
return "", err
}
}
return strings.TrimRight(buf.String(), " \n"), nil
}

View File

@@ -0,0 +1,255 @@
/*
Copyright 2018 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 webhook
import (
"errors"
"fmt"
"log"
"strconv"
"strings"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
webhooktypes "sigs.k8s.io/controller-runtime/pkg/webhook/types"
"sigs.k8s.io/controller-tools/pkg/internal/general"
)
const webhookAnnotationPrefix = "kubebuilder:webhook"
var (
webhookTags = sets.NewString([]string{"groups", "versions", "resources", "verbs", "type", "name", "path", "failure-policy"}...)
serverTags = sets.NewString([]string{"port", "cert-dir", "service", "selector", "secret", "host", "mutating-webhook-config-name", "validating-webhook-config-name"}...)
)
// parseAnnotation parses webhook annotations
func (o *ManifestOptions) parseAnnotation(commentText string) error {
webhookKVMap, serverKVMap := map[string]string{}, map[string]string{}
for _, comment := range strings.Split(commentText, "\n") {
comment := strings.TrimSpace(comment)
anno := general.GetAnnotation(comment, webhookAnnotationPrefix)
if len(anno) == 0 {
continue
}
for _, elem := range strings.Split(anno, ",") {
key, value, err := general.ParseKV(elem)
if err != nil {
log.Fatalf("// +kubebuilder:webhook: tags must be key value pairs. Example "+
"keys [groups=<group1;group2>,resources=<resource1;resource2>,verbs=<verb1;verb2>] "+
"Got string: [%s]", anno)
}
switch {
case webhookTags.Has(key):
webhookKVMap[key] = value
case serverTags.Has(key):
serverKVMap[key] = value
}
}
}
if err := o.parseWebhookAnnotation(webhookKVMap); err != nil {
return err
}
return o.parseServerAnnotation(serverKVMap)
}
// parseWebhookAnnotation parses webhook annotations in the same comment group
// nolint: gocyclo
func (o *ManifestOptions) parseWebhookAnnotation(kvMap map[string]string) error {
if len(kvMap) == 0 {
return nil
}
rule := admissionregistrationv1beta1.RuleWithOperations{}
w := &admission.Webhook{}
for key, value := range kvMap {
switch key {
case "groups":
values := strings.Split(value, ";")
normalized := []string{}
for _, v := range values {
if v == "core" {
normalized = append(normalized, "")
} else {
normalized = append(normalized, v)
}
}
rule.APIGroups = values
case "versions":
values := strings.Split(value, ";")
rule.APIVersions = values
case "resources":
values := strings.Split(value, ";")
rule.Resources = values
case "verbs":
values := strings.Split(value, ";")
var ops []admissionregistrationv1beta1.OperationType
for _, v := range values {
switch strings.ToLower(v) {
case strings.ToLower(string(admissionregistrationv1beta1.Create)):
ops = append(ops, admissionregistrationv1beta1.Create)
case strings.ToLower(string(admissionregistrationv1beta1.Update)):
ops = append(ops, admissionregistrationv1beta1.Update)
case strings.ToLower(string(admissionregistrationv1beta1.Delete)):
ops = append(ops, admissionregistrationv1beta1.Delete)
case strings.ToLower(string(admissionregistrationv1beta1.Connect)):
ops = append(ops, admissionregistrationv1beta1.Connect)
case strings.ToLower(string(admissionregistrationv1beta1.OperationAll)):
ops = append(ops, admissionregistrationv1beta1.OperationAll)
default:
return fmt.Errorf("unknown operation: %v", v)
}
}
rule.Operations = ops
case "type":
switch strings.ToLower(value) {
case "mutating":
w.Type = webhooktypes.WebhookTypeMutating
case "validating":
w.Type = webhooktypes.WebhookTypeValidating
default:
return fmt.Errorf("unknown webhook type: %v", value)
}
case "name":
w.Name = value
case "path":
w.Path = value
case "failure-policy":
switch strings.ToLower(value) {
case strings.ToLower(string(admissionregistrationv1beta1.Ignore)):
fp := admissionregistrationv1beta1.Ignore
w.FailurePolicy = &fp
case strings.ToLower(string(admissionregistrationv1beta1.Fail)):
fp := admissionregistrationv1beta1.Fail
w.FailurePolicy = &fp
default:
return fmt.Errorf("unknown webhook failure policy: %v", value)
}
}
}
w.Rules = []admissionregistrationv1beta1.RuleWithOperations{rule}
w.Handlers = []admission.Handler{admission.HandlerFunc(nil)}
o.webhooks = append(o.webhooks, w)
return nil
}
// parseWebhookAnnotation parses webhook server annotations in the same comment group
// nolint: gocyclo
func (o *ManifestOptions) parseServerAnnotation(kvMap map[string]string) error {
if len(kvMap) == 0 {
return nil
}
for key, value := range kvMap {
switch key {
case "port":
port, err := strconv.Atoi(value)
if err != nil {
return err
}
o.svrOps.Port = int32(port)
case "cert-dir":
o.svrOps.CertDir = value
case "service":
// format: <service=namespace:name>
split := strings.Split(value, ":")
if len(split) != 2 || len(split[0]) == 0 || len(split[1]) == 0 {
return fmt.Errorf("invalid service format: expect <namespace:name>, but got %q", value)
}
if o.svrOps.BootstrapOptions == nil {
o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{}
}
if o.svrOps.Service == nil {
o.svrOps.Service = &webhook.Service{}
}
o.svrOps.Service.Namespace = split[0]
o.svrOps.Service.Name = split[1]
case "selector":
// selector of the service. Format: <selector=label1:value1;label2:value2>
split := strings.Split(value, ";")
if len(split) == 0 {
return fmt.Errorf("invalid selector format: expect <label1:value1;label2:value2>, but got %q", value)
}
if o.svrOps.BootstrapOptions == nil {
o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{}
}
if o.svrOps.Service == nil {
o.svrOps.Service = &webhook.Service{}
}
for _, v := range split {
l := strings.Split(v, ":")
if len(l) != 2 || len(l[0]) == 0 || len(l[1]) == 0 {
return fmt.Errorf("invalid selector format: expect <label1:value1;label2:value2>, but got %q", value)
}
if o.svrOps.Service.Selectors == nil {
o.svrOps.Service.Selectors = map[string]string{}
}
o.svrOps.Service.Selectors[l[0]] = l[1]
}
case "host":
if len(value) == 0 {
return errors.New("host should not be empty if specified")
}
if o.svrOps.BootstrapOptions == nil {
o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{}
}
o.svrOps.Host = &value
case "mutating-webhook-config-name":
if len(value) == 0 {
return errors.New("mutating-webhook-config-name should not be empty if specified")
}
if o.svrOps.BootstrapOptions == nil {
o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{}
}
o.svrOps.MutatingWebhookConfigName = value
case "validating-webhook-config-name":
if len(value) == 0 {
return errors.New("validating-webhook-config-name should not be empty if specified")
}
if o.svrOps.BootstrapOptions == nil {
o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{}
}
o.svrOps.ValidatingWebhookConfigName = value
case "secret":
// format: <secret=namespace:name>
split := strings.Split(value, ":")
if len(split) != 2 || len(split[0]) == 0 || len(split[1]) == 0 {
return fmt.Errorf("invalid secret format: expect <namespace:name>, but got %q", value)
}
if o.svrOps.BootstrapOptions == nil {
o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{}
}
if o.svrOps.Secret == nil {
o.svrOps.Secret = &types.NamespacedName{}
}
o.svrOps.Secret.Namespace = split[0]
o.svrOps.Secret.Name = split[1]
}
}
return nil
}