mirror of https://github.com/docker/cli.git
Merge pull request #1023 from simonferquel/k8s-stack-services-filters
[Kubernetes] stack services filters
This commit is contained in:
commit
0089f172b7
|
@ -76,6 +76,11 @@ func (s *Factory) ReplicaSets() typesappsv1beta2.ReplicaSetInterface {
|
|||
return s.appsClientSet.ReplicaSets(s.namespace)
|
||||
}
|
||||
|
||||
// DaemonSets returns a client for kubernetes daemon sets
|
||||
func (s *Factory) DaemonSets() typesappsv1beta2.DaemonSetInterface {
|
||||
return s.appsClientSet.DaemonSets(s.namespace)
|
||||
}
|
||||
|
||||
// Stacks returns a client for Docker's Stack on Kubernetes
|
||||
func (s *Factory) Stacks(allNamespaces bool) (StackClient, error) {
|
||||
version, err := kubernetes.GetStackAPIVersion(s.clientSet)
|
||||
|
|
|
@ -2,10 +2,13 @@ package kubernetes
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/kubernetes/labels"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
|
@ -65,13 +68,43 @@ func toSwarmProtocol(protocol apiv1.Protocol) swarm.PortConfigProtocol {
|
|||
return swarm.PortConfigProtocol("unknown")
|
||||
}
|
||||
|
||||
func fetchPods(namespace string, pods corev1.PodInterface) ([]apiv1.Pod, error) {
|
||||
labelSelector := labels.SelectorForStack(namespace)
|
||||
podsList, err := pods.List(metav1.ListOptions{LabelSelector: labelSelector})
|
||||
func fetchPods(stackName string, pods corev1.PodInterface, f filters.Args) ([]apiv1.Pod, error) {
|
||||
services := f.Get("service")
|
||||
// for existing script compatibility, support either <servicename> or <stackname>_<servicename> format
|
||||
stackNamePrefix := stackName + "_"
|
||||
for _, s := range services {
|
||||
if strings.HasPrefix(s, stackNamePrefix) {
|
||||
services = append(services, strings.TrimPrefix(s, stackNamePrefix))
|
||||
}
|
||||
}
|
||||
listOpts := metav1.ListOptions{LabelSelector: labels.SelectorForStack(stackName, services...)}
|
||||
var result []apiv1.Pod
|
||||
podsList, err := pods.List(listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return podsList.Items, nil
|
||||
nodes := f.Get("node")
|
||||
for _, pod := range podsList.Items {
|
||||
if filterPod(pod, nodes) &&
|
||||
// name filter is done client side for matching partials
|
||||
f.FuzzyMatch("name", stackNamePrefix+pod.Name) {
|
||||
|
||||
result = append(result, pod)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func filterPod(pod apiv1.Pod, nodes []string) bool {
|
||||
if len(nodes) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, name := range nodes {
|
||||
if pod.Spec.NodeName == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getContainerImage(containers []apiv1.Container) string {
|
||||
|
@ -121,56 +154,76 @@ const (
|
|||
publishedOnRandomPortSuffix = "-random-ports"
|
||||
)
|
||||
|
||||
// Replicas conversion
|
||||
func replicasToServices(replicas *appsv1beta2.ReplicaSetList, services *apiv1.ServiceList) ([]swarm.Service, map[string]formatter.ServiceListInfo, error) {
|
||||
func convertToServices(replicas *appsv1beta2.ReplicaSetList, daemons *appsv1beta2.DaemonSetList, services *apiv1.ServiceList) ([]swarm.Service, map[string]formatter.ServiceListInfo, error) {
|
||||
result := make([]swarm.Service, len(replicas.Items))
|
||||
infos := make(map[string]formatter.ServiceListInfo, len(replicas.Items))
|
||||
infos := make(map[string]formatter.ServiceListInfo, len(replicas.Items)+len(daemons.Items))
|
||||
for i, r := range replicas.Items {
|
||||
serviceName := r.Labels[labels.ForServiceName]
|
||||
serviceHeadless, ok := findService(services, serviceName)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("could not find service '%s'", serviceName)
|
||||
s, err := convertToService(r.Labels[labels.ForServiceName], services, r.Spec.Template.Spec.Containers)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
stack, ok := serviceHeadless.Labels[labels.ForStackName]
|
||||
if ok {
|
||||
stack += "_"
|
||||
}
|
||||
uid := string(serviceHeadless.UID)
|
||||
s := swarm.Service{
|
||||
ID: uid,
|
||||
Spec: swarm.ServiceSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
Name: stack + serviceHeadless.Name,
|
||||
},
|
||||
TaskTemplate: swarm.TaskSpec{
|
||||
ContainerSpec: &swarm.ContainerSpec{
|
||||
Image: getContainerImage(r.Spec.Template.Spec.Containers),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if serviceNodePort, ok := findService(services, serviceName+publishedOnRandomPortSuffix); ok && serviceNodePort.Spec.Type == apiv1.ServiceTypeNodePort {
|
||||
s.Endpoint = serviceEndpoint(serviceNodePort, swarm.PortConfigPublishModeHost)
|
||||
}
|
||||
if serviceLoadBalancer, ok := findService(services, serviceName+publishedServiceSuffix); ok && serviceLoadBalancer.Spec.Type == apiv1.ServiceTypeLoadBalancer {
|
||||
s.Endpoint = serviceEndpoint(serviceLoadBalancer, swarm.PortConfigPublishModeIngress)
|
||||
}
|
||||
result[i] = s
|
||||
infos[uid] = formatter.ServiceListInfo{
|
||||
result[i] = *s
|
||||
infos[s.ID] = formatter.ServiceListInfo{
|
||||
Mode: "replicated",
|
||||
Replicas: fmt.Sprintf("%d/%d", r.Status.AvailableReplicas, r.Status.Replicas),
|
||||
}
|
||||
}
|
||||
for _, d := range daemons.Items {
|
||||
s, err := convertToService(d.Labels[labels.ForServiceName], services, d.Spec.Template.Spec.Containers)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
result = append(result, *s)
|
||||
infos[s.ID] = formatter.ServiceListInfo{
|
||||
Mode: "global",
|
||||
Replicas: fmt.Sprintf("%d/%d", d.Status.NumberReady, d.Status.DesiredNumberScheduled),
|
||||
}
|
||||
}
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].ID < result[j].ID
|
||||
})
|
||||
return result, infos, nil
|
||||
}
|
||||
|
||||
func findService(services *apiv1.ServiceList, name string) (apiv1.Service, bool) {
|
||||
func convertToService(serviceName string, services *apiv1.ServiceList, containers []apiv1.Container) (*swarm.Service, error) {
|
||||
serviceHeadless, err := findService(services, serviceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stack, ok := serviceHeadless.Labels[labels.ForStackName]
|
||||
if ok {
|
||||
stack += "_"
|
||||
}
|
||||
uid := string(serviceHeadless.UID)
|
||||
s := &swarm.Service{
|
||||
ID: uid,
|
||||
Spec: swarm.ServiceSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
Name: stack + serviceHeadless.Name,
|
||||
},
|
||||
TaskTemplate: swarm.TaskSpec{
|
||||
ContainerSpec: &swarm.ContainerSpec{
|
||||
Image: getContainerImage(containers),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if serviceNodePort, err := findService(services, serviceName+publishedOnRandomPortSuffix); err == nil && serviceNodePort.Spec.Type == apiv1.ServiceTypeNodePort {
|
||||
s.Endpoint = serviceEndpoint(serviceNodePort, swarm.PortConfigPublishModeHost)
|
||||
}
|
||||
if serviceLoadBalancer, err := findService(services, serviceName+publishedServiceSuffix); err == nil && serviceLoadBalancer.Spec.Type == apiv1.ServiceTypeLoadBalancer {
|
||||
s.Endpoint = serviceEndpoint(serviceLoadBalancer, swarm.PortConfigPublishModeIngress)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func findService(services *apiv1.ServiceList, name string) (apiv1.Service, error) {
|
||||
for _, s := range services.Items {
|
||||
if s.Name == name {
|
||||
return s, true
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
return apiv1.Service{}, false
|
||||
return apiv1.Service{}, fmt.Errorf("could not find service '%s'", name)
|
||||
}
|
||||
|
||||
func serviceEndpoint(service apiv1.Service, publishMode swarm.PortConfigPublishMode) swarm.Endpoint {
|
||||
|
|
|
@ -19,7 +19,7 @@ func TestReplicasConversionNeedsAService(t *testing.T) {
|
|||
Items: []appsv1beta2.ReplicaSet{makeReplicaSet("unknown", 0, 0)},
|
||||
}
|
||||
services := apiv1.ServiceList{}
|
||||
_, _, err := replicasToServices(&replicas, &services)
|
||||
_, _, err := convertToServices(&replicas, &appsv1beta2.DaemonSetList{}, &services)
|
||||
assert.ErrorContains(t, err, "could not find service")
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ func TestKubernetesServiceToSwarmServiceConversion(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
swarmServices, listInfo, err := replicasToServices(tc.replicas, tc.services)
|
||||
swarmServices, listInfo, err := convertToServices(tc.replicas, &appsv1beta2.DaemonSetList{}, tc.services)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, tc.expectedServices, swarmServices)
|
||||
assert.DeepEqual(t, tc.expectedListInfo, listInfo)
|
||||
|
|
|
@ -10,17 +10,23 @@ import (
|
|||
"github.com/docker/cli/cli/command/task"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
var supportedPSFilters = map[string]bool{
|
||||
"name": true,
|
||||
"service": true,
|
||||
"node": true,
|
||||
}
|
||||
|
||||
// RunPS is the kubernetes implementation of docker stack ps
|
||||
func RunPS(dockerCli *KubeCli, options options.PS) error {
|
||||
namespace := options.Namespace
|
||||
|
||||
// Initialize clients
|
||||
filters := options.Filter.Value()
|
||||
if err := filters.Validate(supportedPSFilters); err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := dockerCli.composeClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -29,78 +35,71 @@ func RunPS(dockerCli *KubeCli, options options.PS) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
podsClient := client.Pods()
|
||||
|
||||
// Fetch pods
|
||||
if _, err := stacks.Get(namespace); err != nil {
|
||||
return fmt.Errorf("nothing found in stack: %s", namespace)
|
||||
stackName := options.Namespace
|
||||
_, err = stacks.Get(stackName)
|
||||
if apierrs.IsNotFound(err) {
|
||||
return fmt.Errorf("nothing found in stack: %s", stackName)
|
||||
}
|
||||
|
||||
pods, err := fetchPods(namespace, podsClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pods) == 0 {
|
||||
return fmt.Errorf("nothing found in stack: %s", namespace)
|
||||
pods, err := fetchPods(stackName, client.Pods(), filters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(pods) == 0 {
|
||||
return fmt.Errorf("nothing found in stack: %s", stackName)
|
||||
}
|
||||
return printTasks(dockerCli, options, stackName, client, pods)
|
||||
}
|
||||
|
||||
func printTasks(dockerCli command.Cli, options options.PS, namespace string, client corev1.NodesGetter, pods []apiv1.Pod) error {
|
||||
format := options.Format
|
||||
if len(format) == 0 {
|
||||
if format == "" {
|
||||
format = task.DefaultFormat(dockerCli.ConfigFile(), options.Quiet)
|
||||
}
|
||||
nodeResolver := makeNodeResolver(options.NoResolve, client.Nodes())
|
||||
|
||||
tasks := make([]swarm.Task, len(pods))
|
||||
for i, pod := range pods {
|
||||
tasks[i] = podToTask(pod)
|
||||
}
|
||||
return print(dockerCli, namespace, tasks, pods, nodeResolver, !options.NoTrunc, options.Quiet, format)
|
||||
}
|
||||
|
||||
type idResolver func(name string) (string, error)
|
||||
|
||||
func print(dockerCli command.Cli, namespace string, tasks []swarm.Task, pods []apiv1.Pod, nodeResolver idResolver, trunc, quiet bool, format string) error {
|
||||
sort.Stable(tasksBySlot(tasks))
|
||||
|
||||
names := map[string]string{}
|
||||
nodes := map[string]string{}
|
||||
|
||||
tasksCtx := formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: formatter.NewTaskFormat(format, quiet),
|
||||
Trunc: trunc,
|
||||
n, err := client.Nodes().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, task := range tasks {
|
||||
nodeValue, err := nodeResolver(pods[i].Spec.NodeName)
|
||||
nodeValue, err := resolveNode(pods[i].Spec.NodeName, n, options.NoResolve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
names[task.ID] = fmt.Sprintf("%s_%s", namespace, pods[i].Name)
|
||||
nodes[task.ID] = nodeValue
|
||||
}
|
||||
|
||||
tasksCtx := formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: formatter.NewTaskFormat(format, options.Quiet),
|
||||
Trunc: !options.NoTrunc,
|
||||
}
|
||||
|
||||
return formatter.TaskWrite(tasksCtx, tasks, names, nodes)
|
||||
}
|
||||
|
||||
func makeNodeResolver(noResolve bool, nodes corev1.NodeInterface) func(string) (string, error) {
|
||||
func resolveNode(name string, nodes *apiv1.NodeList, noResolve bool) (string, error) {
|
||||
// Here we have a name and we need to resolve its identifier. To mimic swarm behavior
|
||||
// we need to resolve the id when noresolve is set, otherwise we return the name.
|
||||
// we need to resolve to the id when noResolve is set, otherwise we return the name.
|
||||
if noResolve {
|
||||
return func(name string) (string, error) {
|
||||
n, err := nodes.List(metav1.ListOptions{
|
||||
FieldSelector: fields.OneTermEqualSelector(api.ObjectNameField, name).String(),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
for _, node := range nodes.Items {
|
||||
if node.Name == name {
|
||||
return string(node.UID), nil
|
||||
}
|
||||
if len(n.Items) != 1 {
|
||||
return "", fmt.Errorf("could not find node '%s'", name)
|
||||
}
|
||||
return string(n.Items[0].UID), nil
|
||||
}
|
||||
return "", fmt.Errorf("could not find node '%s'", name)
|
||||
}
|
||||
return func(name string) (string, error) { return name, nil }
|
||||
return name, nil
|
||||
}
|
||||
|
|
|
@ -2,43 +2,121 @@ package kubernetes
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/command/stack/options"
|
||||
"github.com/docker/cli/kubernetes/labels"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var supportedServicesFilters = map[string]bool{
|
||||
"mode": true,
|
||||
"name": true,
|
||||
"label": true,
|
||||
}
|
||||
|
||||
func generateSelector(labels map[string][]string) []string {
|
||||
var result []string
|
||||
for k, v := range labels {
|
||||
for _, val := range v {
|
||||
result = append(result, fmt.Sprintf("%s=%s", k, val))
|
||||
}
|
||||
if len(v) == 0 {
|
||||
result = append(result, k)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func parseLabelFilters(rawFilters []string) map[string][]string {
|
||||
labels := map[string][]string{}
|
||||
for _, rawLabel := range rawFilters {
|
||||
v := strings.SplitN(rawLabel, "=", 2)
|
||||
key := v[0]
|
||||
if len(v) > 1 {
|
||||
labels[key] = append(labels[key], v[1])
|
||||
} else if _, ok := labels[key]; !ok {
|
||||
labels[key] = []string{}
|
||||
}
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
||||
func generateLabelSelector(f filters.Args, stackName string) string {
|
||||
names := f.Get("name")
|
||||
sort.Strings(names)
|
||||
for _, n := range names {
|
||||
if strings.HasPrefix(n, stackName+"_") {
|
||||
// also accepts with unprefixed service name (for compat with existing swarm scripts where service names are prefixed by stack names)
|
||||
names = append(names, strings.TrimPrefix(n, stackName+"_"))
|
||||
}
|
||||
}
|
||||
selectors := append(generateSelector(parseLabelFilters(f.Get("label"))), labels.SelectorForStack(stackName, names...))
|
||||
return strings.Join(selectors, ",")
|
||||
}
|
||||
|
||||
func getResourcesForServiceList(dockerCli *KubeCli, filters filters.Args, labelSelector string) (*appsv1beta2.ReplicaSetList, *appsv1beta2.DaemonSetList, *corev1.ServiceList, error) {
|
||||
client, err := dockerCli.composeClient()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
modes := filters.Get("mode")
|
||||
replicas := &appsv1beta2.ReplicaSetList{}
|
||||
if len(modes) == 0 || filters.ExactMatch("mode", "replicated") {
|
||||
if replicas, err = client.ReplicaSets().List(metav1.ListOptions{LabelSelector: labelSelector}); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
daemons := &appsv1beta2.DaemonSetList{}
|
||||
if len(modes) == 0 || filters.ExactMatch("mode", "global") {
|
||||
if daemons, err = client.DaemonSets().List(metav1.ListOptions{LabelSelector: labelSelector}); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
services, err := client.Services().List(metav1.ListOptions{LabelSelector: labelSelector})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return replicas, daemons, services, nil
|
||||
}
|
||||
|
||||
// RunServices is the kubernetes implementation of docker stack services
|
||||
func RunServices(dockerCli *KubeCli, opts options.Services) error {
|
||||
// Initialize clients
|
||||
filters := opts.Filter.Value()
|
||||
if err := filters.Validate(supportedServicesFilters); err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := dockerCli.composeClient()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
stacks, err := client.Stacks(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
replicas := client.ReplicaSets()
|
||||
|
||||
if _, err := stacks.Get(opts.Namespace); err != nil {
|
||||
fmt.Fprintf(dockerCli.Err(), "Nothing found in stack: %s\n", opts.Namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
replicasList, err := replicas.List(metav1.ListOptions{LabelSelector: labels.SelectorForStack(opts.Namespace)})
|
||||
stackName := opts.Namespace
|
||||
_, err = stacks.Get(stackName)
|
||||
if apierrs.IsNotFound(err) {
|
||||
return fmt.Errorf("nothing found in stack: %s", stackName)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
servicesList, err := client.Services().List(metav1.ListOptions{LabelSelector: labels.SelectorForStack(opts.Namespace)})
|
||||
labelSelector := generateLabelSelector(filters, stackName)
|
||||
replicasList, daemonsList, servicesList, err := getResourcesForServiceList(dockerCli, filters, labelSelector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Convert Replicas sets and kubernetes services to swam services and formatter informations
|
||||
services, info, err := replicasToServices(replicasList, servicesList)
|
||||
services, info, err := convertToServices(replicasList, daemonsList, servicesList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/gotestyourself/gotestyourself/assert/cmp"
|
||||
)
|
||||
|
||||
func TestServiceFiltersLabelSelectorGen(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
stackName string
|
||||
filters filters.Args
|
||||
expectedSelectorParts []string
|
||||
}{
|
||||
{
|
||||
name: "no-filter",
|
||||
stackName: "test",
|
||||
filters: filters.NewArgs(),
|
||||
expectedSelectorParts: []string{
|
||||
"com.docker.stack.namespace=test",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single-name filter",
|
||||
stackName: "test",
|
||||
filters: filters.NewArgs(filters.KeyValuePair{Key: "name", Value: "svc-test"}),
|
||||
expectedSelectorParts: []string{
|
||||
"com.docker.stack.namespace=test",
|
||||
"com.docker.service.name=svc-test",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi-name filter",
|
||||
stackName: "test",
|
||||
filters: filters.NewArgs(
|
||||
filters.KeyValuePair{Key: "name", Value: "svc-test"},
|
||||
filters.KeyValuePair{Key: "name", Value: "svc-test2"},
|
||||
),
|
||||
expectedSelectorParts: []string{
|
||||
"com.docker.stack.namespace=test",
|
||||
"com.docker.service.name in (svc-test,svc-test2)",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "label present filter",
|
||||
stackName: "test",
|
||||
filters: filters.NewArgs(
|
||||
filters.KeyValuePair{Key: "label", Value: "label-is-present"},
|
||||
),
|
||||
expectedSelectorParts: []string{
|
||||
"com.docker.stack.namespace=test",
|
||||
"label-is-present",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single value label filter",
|
||||
stackName: "test",
|
||||
filters: filters.NewArgs(
|
||||
filters.KeyValuePair{Key: "label", Value: "label1=test"},
|
||||
),
|
||||
expectedSelectorParts: []string{
|
||||
"com.docker.stack.namespace=test",
|
||||
"label1=test",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi value label filter",
|
||||
stackName: "test",
|
||||
filters: filters.NewArgs(
|
||||
filters.KeyValuePair{Key: "label", Value: "label1=test"},
|
||||
filters.KeyValuePair{Key: "label", Value: "label1=test2"},
|
||||
),
|
||||
expectedSelectorParts: []string{
|
||||
"com.docker.stack.namespace=test",
|
||||
"label1=test",
|
||||
"label1=test2",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "2 different labels filter",
|
||||
stackName: "test",
|
||||
filters: filters.NewArgs(
|
||||
filters.KeyValuePair{Key: "label", Value: "label1=test"},
|
||||
filters.KeyValuePair{Key: "label", Value: "label2=test2"},
|
||||
),
|
||||
expectedSelectorParts: []string{
|
||||
"com.docker.stack.namespace=test",
|
||||
"label1=test",
|
||||
"label2=test2",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "name filter with stackName prefix",
|
||||
stackName: "test",
|
||||
filters: filters.NewArgs(
|
||||
filters.KeyValuePair{Key: "name", Value: "test_svc1"},
|
||||
),
|
||||
expectedSelectorParts: []string{
|
||||
"com.docker.stack.namespace=test",
|
||||
"com.docker.service.name in (test_svc1,svc1)",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
result := generateLabelSelector(c.filters, c.stackName)
|
||||
for _, toFind := range c.expectedSelectorParts {
|
||||
assert.Assert(t, cmp.Contains(result, toFind))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -37,7 +37,6 @@ func newPsCommand(dockerCli command.Cli) *cobra.Command {
|
|||
flags.BoolVar(&opts.NoTrunc, "no-trunc", false, "Do not truncate output")
|
||||
flags.BoolVar(&opts.NoResolve, "no-resolve", false, "Do not map IDs to Names")
|
||||
flags.VarP(&opts.Filter, "filter", "f", "Filter output based on conditions provided")
|
||||
flags.SetAnnotation("filter", "swarm", nil)
|
||||
flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display task IDs")
|
||||
flags.StringVar(&opts.Format, "format", "", "Pretty-print tasks using a Go template")
|
||||
kubernetes.AddNamespaceFlag(flags)
|
||||
|
|
|
@ -37,7 +37,6 @@ func newServicesCommand(dockerCli command.Cli) *cobra.Command {
|
|||
flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")
|
||||
flags.StringVar(&opts.Format, "format", "", "Pretty-print services using a Go template")
|
||||
flags.VarP(&opts.Filter, "filter", "f", "Filter output based on conditions provided")
|
||||
flags.SetAnnotation("filter", "swarm", nil)
|
||||
kubernetes.AddNamespaceFlag(flags)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -64,8 +64,23 @@ dn7m7nhhfb9y myapp_db 1/1 mysql@sha256:a9a5b559f8821fe73d58c3606c8
|
|||
The currently supported filters are:
|
||||
|
||||
* id / ID (`--filter id=7be5ei6sqeye`, or `--filter ID=7be5ei6sqeye`)
|
||||
* name (`--filter name=myapp_web`)
|
||||
* Swarm: supported
|
||||
* Kubernetes: not supported
|
||||
* label (`--filter label=key=value`)
|
||||
* Swarm: supported
|
||||
* Kubernetes: supported
|
||||
* mode (`--filter mode=replicated`, or `--filter mode=global`)
|
||||
* Swarm: not supported
|
||||
* Kubernetes: supported
|
||||
* name (`--filter name=myapp_web`)
|
||||
* Swarm: supported
|
||||
* Kubernetes: supported
|
||||
* node (`--filter node=mynode`)
|
||||
* Swarm: not supported
|
||||
* Kubernetes: supported
|
||||
* service (`--filter service=web`)
|
||||
* Swarm: not supported
|
||||
* Kubernetes: supported
|
||||
|
||||
### Formatting
|
||||
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 announced contains tools for announcing API group factories. This is
|
||||
// distinct from registration (in the 'registered' package) in that it's safe
|
||||
// to announce every possible group linked in, but only groups requested at
|
||||
// runtime should be registered. This package contains both a registry, and
|
||||
// factory code (which was formerly copy-pasta in every install package).
|
||||
package announced
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// APIGroupFactoryRegistry allows for groups and versions to announce themselves,
|
||||
// which simply makes them available and doesn't take other actions. Later,
|
||||
// users of the registry can select which groups and versions they'd actually
|
||||
// like to register with an APIRegistrationManager.
|
||||
//
|
||||
// (Right now APIRegistrationManager has separate 'registration' and 'enabled'
|
||||
// concepts-- APIGroupFactory is going to take over the former function;
|
||||
// they will overlap untill the refactoring is finished.)
|
||||
//
|
||||
// The key is the group name. After initialization, this should be treated as
|
||||
// read-only. It is implemented as a map from group name to group factory, and
|
||||
// it is safe to use this knowledge to manually pick out groups to register
|
||||
// (e.g., for testing).
|
||||
type APIGroupFactoryRegistry map[string]*GroupMetaFactory
|
||||
|
||||
func (gar APIGroupFactoryRegistry) group(groupName string) *GroupMetaFactory {
|
||||
gmf, ok := gar[groupName]
|
||||
if !ok {
|
||||
gmf = &GroupMetaFactory{VersionArgs: map[string]*GroupVersionFactoryArgs{}}
|
||||
gar[groupName] = gmf
|
||||
}
|
||||
return gmf
|
||||
}
|
||||
|
||||
// AnnounceGroupVersion adds the particular arguments for this group version to the group factory.
|
||||
func (gar APIGroupFactoryRegistry) AnnounceGroupVersion(gvf *GroupVersionFactoryArgs) error {
|
||||
gmf := gar.group(gvf.GroupName)
|
||||
if _, ok := gmf.VersionArgs[gvf.VersionName]; ok {
|
||||
return fmt.Errorf("version %q in group %q has already been announced", gvf.VersionName, gvf.GroupName)
|
||||
}
|
||||
gmf.VersionArgs[gvf.VersionName] = gvf
|
||||
return nil
|
||||
}
|
||||
|
||||
// AnnounceGroup adds the group-wide arguments to the group factory.
|
||||
func (gar APIGroupFactoryRegistry) AnnounceGroup(args *GroupMetaFactoryArgs) error {
|
||||
gmf := gar.group(args.GroupName)
|
||||
if gmf.GroupArgs != nil {
|
||||
return fmt.Errorf("group %q has already been announced", args.GroupName)
|
||||
}
|
||||
gmf.GroupArgs = args
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterAndEnableAll throws every factory at the specified API registration
|
||||
// manager, and lets it decide which to register. (If you want to do this a la
|
||||
// cart, you may look through gar itself-- it's just a map.)
|
||||
func (gar APIGroupFactoryRegistry) RegisterAndEnableAll(m *registered.APIRegistrationManager, scheme *runtime.Scheme) error {
|
||||
for groupName, gmf := range gar {
|
||||
if err := gmf.Register(m); err != nil {
|
||||
return fmt.Errorf("error registering %v: %v", groupName, err)
|
||||
}
|
||||
if err := gmf.Enable(m, scheme); err != nil {
|
||||
return fmt.Errorf("error enabling %v: %v", groupName, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AnnouncePreconstructedFactory announces a factory which you've manually assembled.
|
||||
// You may call this instead of calling AnnounceGroup and AnnounceGroupVersion.
|
||||
func (gar APIGroupFactoryRegistry) AnnouncePreconstructedFactory(gmf *GroupMetaFactory) error {
|
||||
name := gmf.GroupArgs.GroupName
|
||||
if _, exists := gar[name]; exists {
|
||||
return fmt.Errorf("the group %q has already been announced.", name)
|
||||
}
|
||||
gar[name] = gmf
|
||||
return nil
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 announced
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apimachinery"
|
||||
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
type SchemeFunc func(*runtime.Scheme) error
|
||||
type VersionToSchemeFunc map[string]SchemeFunc
|
||||
|
||||
// GroupVersionFactoryArgs contains all the per-version parts of a GroupMetaFactory.
|
||||
type GroupVersionFactoryArgs struct {
|
||||
GroupName string
|
||||
VersionName string
|
||||
|
||||
AddToScheme SchemeFunc
|
||||
}
|
||||
|
||||
// GroupMetaFactoryArgs contains the group-level args of a GroupMetaFactory.
|
||||
type GroupMetaFactoryArgs struct {
|
||||
// GroupName is the name of the API-Group
|
||||
//
|
||||
// example: 'servicecatalog.k8s.io'
|
||||
GroupName string
|
||||
VersionPreferenceOrder []string
|
||||
// RootScopedKinds are resources that are not namespaced.
|
||||
RootScopedKinds sets.String // nil is allowed
|
||||
IgnoredKinds sets.String // nil is allowed
|
||||
|
||||
// May be nil if there are no internal objects.
|
||||
AddInternalObjectsToScheme SchemeFunc
|
||||
}
|
||||
|
||||
// NewGroupMetaFactory builds the args for you. This is for if you're
|
||||
// constructing a factory all at once and not using the registry.
|
||||
func NewGroupMetaFactory(groupArgs *GroupMetaFactoryArgs, versions VersionToSchemeFunc) *GroupMetaFactory {
|
||||
gmf := &GroupMetaFactory{
|
||||
GroupArgs: groupArgs,
|
||||
VersionArgs: map[string]*GroupVersionFactoryArgs{},
|
||||
}
|
||||
for v, f := range versions {
|
||||
gmf.VersionArgs[v] = &GroupVersionFactoryArgs{
|
||||
GroupName: groupArgs.GroupName,
|
||||
VersionName: v,
|
||||
AddToScheme: f,
|
||||
}
|
||||
}
|
||||
return gmf
|
||||
}
|
||||
|
||||
// Announce adds this Group factory to the global factory registry. It should
|
||||
// only be called if you constructed the GroupMetaFactory yourself via
|
||||
// NewGroupMetaFactory.
|
||||
// Note that this will panic on an error, since it's expected that you'll be
|
||||
// calling this at initialization time and any error is a result of a
|
||||
// programmer importing the wrong set of packages. If this assumption doesn't
|
||||
// work for you, just call DefaultGroupFactoryRegistry.AnnouncePreconstructedFactory
|
||||
// yourself.
|
||||
func (gmf *GroupMetaFactory) Announce(groupFactoryRegistry APIGroupFactoryRegistry) *GroupMetaFactory {
|
||||
if err := groupFactoryRegistry.AnnouncePreconstructedFactory(gmf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return gmf
|
||||
}
|
||||
|
||||
// GroupMetaFactory has the logic for actually assembling and registering a group.
|
||||
//
|
||||
// There are two ways of obtaining one of these.
|
||||
// 1. You can announce your group and versions separately, and then let the
|
||||
// GroupFactoryRegistry assemble this object for you. (This allows group and
|
||||
// versions to be imported separately, without referencing each other, to
|
||||
// keep import trees small.)
|
||||
// 2. You can call NewGroupMetaFactory(), which is mostly a drop-in replacement
|
||||
// for the old, bad way of doing things. You can then call .Announce() to
|
||||
// announce your constructed factory to any code that would like to do
|
||||
// things the new, better way.
|
||||
//
|
||||
// Note that GroupMetaFactory actually does construct GroupMeta objects, but
|
||||
// currently it does so in a way that's very entangled with an
|
||||
// APIRegistrationManager. It's a TODO item to cleanly separate that interface.
|
||||
type GroupMetaFactory struct {
|
||||
GroupArgs *GroupMetaFactoryArgs
|
||||
// map of version name to version factory
|
||||
VersionArgs map[string]*GroupVersionFactoryArgs
|
||||
|
||||
// assembled by Register()
|
||||
prioritizedVersionList []schema.GroupVersion
|
||||
}
|
||||
|
||||
// Register constructs the finalized prioritized version list and sanity checks
|
||||
// the announced group & versions. Then it calls register.
|
||||
func (gmf *GroupMetaFactory) Register(m *registered.APIRegistrationManager) error {
|
||||
if gmf.GroupArgs == nil {
|
||||
return fmt.Errorf("partially announced groups are not allowed, only got versions: %#v", gmf.VersionArgs)
|
||||
}
|
||||
if len(gmf.VersionArgs) == 0 {
|
||||
return fmt.Errorf("group %v announced but no versions announced", gmf.GroupArgs.GroupName)
|
||||
}
|
||||
|
||||
pvSet := sets.NewString(gmf.GroupArgs.VersionPreferenceOrder...)
|
||||
if pvSet.Len() != len(gmf.GroupArgs.VersionPreferenceOrder) {
|
||||
return fmt.Errorf("preference order for group %v has duplicates: %v", gmf.GroupArgs.GroupName, gmf.GroupArgs.VersionPreferenceOrder)
|
||||
}
|
||||
prioritizedVersions := []schema.GroupVersion{}
|
||||
for _, v := range gmf.GroupArgs.VersionPreferenceOrder {
|
||||
prioritizedVersions = append(
|
||||
prioritizedVersions,
|
||||
schema.GroupVersion{
|
||||
Group: gmf.GroupArgs.GroupName,
|
||||
Version: v,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Go through versions that weren't explicitly prioritized.
|
||||
unprioritizedVersions := []schema.GroupVersion{}
|
||||
for _, v := range gmf.VersionArgs {
|
||||
if v.GroupName != gmf.GroupArgs.GroupName {
|
||||
return fmt.Errorf("found %v/%v in group %v?", v.GroupName, v.VersionName, gmf.GroupArgs.GroupName)
|
||||
}
|
||||
if pvSet.Has(v.VersionName) {
|
||||
pvSet.Delete(v.VersionName)
|
||||
continue
|
||||
}
|
||||
unprioritizedVersions = append(unprioritizedVersions, schema.GroupVersion{Group: v.GroupName, Version: v.VersionName})
|
||||
}
|
||||
if len(unprioritizedVersions) > 1 {
|
||||
glog.Warningf("group %v has multiple unprioritized versions: %#v. They will have an arbitrary preference order!", gmf.GroupArgs.GroupName, unprioritizedVersions)
|
||||
}
|
||||
if pvSet.Len() != 0 {
|
||||
return fmt.Errorf("group %v has versions in the priority list that were never announced: %s", gmf.GroupArgs.GroupName, pvSet)
|
||||
}
|
||||
prioritizedVersions = append(prioritizedVersions, unprioritizedVersions...)
|
||||
m.RegisterVersions(prioritizedVersions)
|
||||
gmf.prioritizedVersionList = prioritizedVersions
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gmf *GroupMetaFactory) newRESTMapper(scheme *runtime.Scheme, externalVersions []schema.GroupVersion, groupMeta *apimachinery.GroupMeta) meta.RESTMapper {
|
||||
// the list of kinds that are scoped at the root of the api hierarchy
|
||||
// if a kind is not enumerated here, it is assumed to have a namespace scope
|
||||
rootScoped := sets.NewString()
|
||||
if gmf.GroupArgs.RootScopedKinds != nil {
|
||||
rootScoped = gmf.GroupArgs.RootScopedKinds
|
||||
}
|
||||
ignoredKinds := sets.NewString()
|
||||
if gmf.GroupArgs.IgnoredKinds != nil {
|
||||
ignoredKinds = gmf.GroupArgs.IgnoredKinds
|
||||
}
|
||||
|
||||
mapper := meta.NewDefaultRESTMapper(externalVersions, groupMeta.InterfacesFor)
|
||||
for _, gv := range externalVersions {
|
||||
for kind := range scheme.KnownTypes(gv) {
|
||||
if ignoredKinds.Has(kind) {
|
||||
continue
|
||||
}
|
||||
scope := meta.RESTScopeNamespace
|
||||
if rootScoped.Has(kind) {
|
||||
scope = meta.RESTScopeRoot
|
||||
}
|
||||
mapper.Add(gv.WithKind(kind), scope)
|
||||
}
|
||||
}
|
||||
|
||||
return mapper
|
||||
}
|
||||
|
||||
// Enable enables group versions that are allowed, adds methods to the scheme, etc.
|
||||
func (gmf *GroupMetaFactory) Enable(m *registered.APIRegistrationManager, scheme *runtime.Scheme) error {
|
||||
externalVersions := []schema.GroupVersion{}
|
||||
for _, v := range gmf.prioritizedVersionList {
|
||||
if !m.IsAllowedVersion(v) {
|
||||
continue
|
||||
}
|
||||
externalVersions = append(externalVersions, v)
|
||||
if err := m.EnableVersions(v); err != nil {
|
||||
return err
|
||||
}
|
||||
gmf.VersionArgs[v.Version].AddToScheme(scheme)
|
||||
}
|
||||
if len(externalVersions) == 0 {
|
||||
glog.V(4).Infof("No version is registered for group %v", gmf.GroupArgs.GroupName)
|
||||
return nil
|
||||
}
|
||||
|
||||
if gmf.GroupArgs.AddInternalObjectsToScheme != nil {
|
||||
gmf.GroupArgs.AddInternalObjectsToScheme(scheme)
|
||||
}
|
||||
|
||||
preferredExternalVersion := externalVersions[0]
|
||||
accessor := meta.NewAccessor()
|
||||
|
||||
groupMeta := &apimachinery.GroupMeta{
|
||||
GroupVersion: preferredExternalVersion,
|
||||
GroupVersions: externalVersions,
|
||||
SelfLinker: runtime.SelfLinker(accessor),
|
||||
}
|
||||
for _, v := range externalVersions {
|
||||
gvf := gmf.VersionArgs[v.Version]
|
||||
if err := groupMeta.AddVersionInterfaces(
|
||||
schema.GroupVersion{Group: gvf.GroupName, Version: gvf.VersionName},
|
||||
&meta.VersionInterfaces{
|
||||
ObjectConvertor: scheme,
|
||||
MetadataAccessor: accessor,
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
groupMeta.InterfacesFor = groupMeta.DefaultInterfacesFor
|
||||
groupMeta.RESTMapper = gmf.newRESTMapper(scheme, externalVersions, groupMeta)
|
||||
|
||||
if err := m.RegisterGroup(*groupMeta); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterAndEnable is provided only to allow this code to get added in multiple steps.
|
||||
// It's really bad that this is called in init() methods, but supporting this
|
||||
// temporarily lets us do the change incrementally.
|
||||
func (gmf *GroupMetaFactory) RegisterAndEnable(registry *registered.APIRegistrationManager, scheme *runtime.Scheme) error {
|
||||
if err := gmf.Register(registry); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gmf.Enable(registry, scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 apimachinery contains the generic API machinery code that
|
||||
// is common to both server and clients.
|
||||
// This package should never import specific API objects.
|
||||
package apimachinery // import "k8s.io/apimachinery/pkg/apimachinery"
|
|
@ -1,336 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 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 to keep track of API Versions that can be registered and are enabled in api.Scheme.
|
||||
package registered
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apimachinery"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// APIRegistrationManager provides the concept of what API groups are enabled.
|
||||
//
|
||||
// TODO: currently, it also provides a "registered" concept. But it's wrong to
|
||||
// have both concepts in the same object. Therefore the "announced" package is
|
||||
// going to take over the registered concept. After all the install packages
|
||||
// are switched to using the announce package instead of this package, then we
|
||||
// can combine the registered/enabled concepts in this object. Simplifying this
|
||||
// isn't easy right now because there are so many callers of this package.
|
||||
type APIRegistrationManager struct {
|
||||
// registeredGroupVersions stores all API group versions for which RegisterGroup is called.
|
||||
registeredVersions map[schema.GroupVersion]struct{}
|
||||
|
||||
// enabledVersions represents all enabled API versions. It should be a
|
||||
// subset of registeredVersions. Please call EnableVersions() to add
|
||||
// enabled versions.
|
||||
enabledVersions map[schema.GroupVersion]struct{}
|
||||
|
||||
// map of group meta for all groups.
|
||||
groupMetaMap map[string]*apimachinery.GroupMeta
|
||||
|
||||
// envRequestedVersions represents the versions requested via the
|
||||
// KUBE_API_VERSIONS environment variable. The install package of each group
|
||||
// checks this list before add their versions to the latest package and
|
||||
// Scheme. This list is small and order matters, so represent as a slice
|
||||
envRequestedVersions []schema.GroupVersion
|
||||
}
|
||||
|
||||
// NewAPIRegistrationManager constructs a new manager. The argument ought to be
|
||||
// the value of the KUBE_API_VERSIONS env var, or a value of this which you
|
||||
// wish to test.
|
||||
func NewAPIRegistrationManager(kubeAPIVersions string) (*APIRegistrationManager, error) {
|
||||
m := &APIRegistrationManager{
|
||||
registeredVersions: map[schema.GroupVersion]struct{}{},
|
||||
enabledVersions: map[schema.GroupVersion]struct{}{},
|
||||
groupMetaMap: map[string]*apimachinery.GroupMeta{},
|
||||
envRequestedVersions: []schema.GroupVersion{},
|
||||
}
|
||||
|
||||
if len(kubeAPIVersions) != 0 {
|
||||
for _, version := range strings.Split(kubeAPIVersions, ",") {
|
||||
gv, err := schema.ParseGroupVersion(version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid api version: %s in KUBE_API_VERSIONS: %s.",
|
||||
version, kubeAPIVersions)
|
||||
}
|
||||
m.envRequestedVersions = append(m.envRequestedVersions, gv)
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func NewOrDie(kubeAPIVersions string) *APIRegistrationManager {
|
||||
m, err := NewAPIRegistrationManager(kubeAPIVersions)
|
||||
if err != nil {
|
||||
glog.Fatalf("Could not construct version manager: %v (KUBE_API_VERSIONS=%q)", err, kubeAPIVersions)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// RegisterVersions adds the given group versions to the list of registered group versions.
|
||||
func (m *APIRegistrationManager) RegisterVersions(availableVersions []schema.GroupVersion) {
|
||||
for _, v := range availableVersions {
|
||||
m.registeredVersions[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterGroup adds the given group to the list of registered groups.
|
||||
func (m *APIRegistrationManager) RegisterGroup(groupMeta apimachinery.GroupMeta) error {
|
||||
groupName := groupMeta.GroupVersion.Group
|
||||
if _, found := m.groupMetaMap[groupName]; found {
|
||||
return fmt.Errorf("group %q is already registered in groupsMap: %v", groupName, m.groupMetaMap)
|
||||
}
|
||||
m.groupMetaMap[groupName] = &groupMeta
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableVersions adds the versions for the given group to the list of enabled versions.
|
||||
// Note that the caller should call RegisterGroup before calling this method.
|
||||
// The caller of this function is responsible to add the versions to scheme and RESTMapper.
|
||||
func (m *APIRegistrationManager) EnableVersions(versions ...schema.GroupVersion) error {
|
||||
var unregisteredVersions []schema.GroupVersion
|
||||
for _, v := range versions {
|
||||
if _, found := m.registeredVersions[v]; !found {
|
||||
unregisteredVersions = append(unregisteredVersions, v)
|
||||
}
|
||||
m.enabledVersions[v] = struct{}{}
|
||||
}
|
||||
if len(unregisteredVersions) != 0 {
|
||||
return fmt.Errorf("Please register versions before enabling them: %v", unregisteredVersions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsAllowedVersion returns if the version is allowed by the KUBE_API_VERSIONS
|
||||
// environment variable. If the environment variable is empty, then it always
|
||||
// returns true.
|
||||
func (m *APIRegistrationManager) IsAllowedVersion(v schema.GroupVersion) bool {
|
||||
if len(m.envRequestedVersions) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, envGV := range m.envRequestedVersions {
|
||||
if v == envGV {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsEnabledVersion returns if a version is enabled.
|
||||
func (m *APIRegistrationManager) IsEnabledVersion(v schema.GroupVersion) bool {
|
||||
_, found := m.enabledVersions[v]
|
||||
return found
|
||||
}
|
||||
|
||||
// EnabledVersions returns all enabled versions. Groups are randomly ordered, but versions within groups
|
||||
// are priority order from best to worst
|
||||
func (m *APIRegistrationManager) EnabledVersions() []schema.GroupVersion {
|
||||
ret := []schema.GroupVersion{}
|
||||
for _, groupMeta := range m.groupMetaMap {
|
||||
for _, version := range groupMeta.GroupVersions {
|
||||
if m.IsEnabledVersion(version) {
|
||||
ret = append(ret, version)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// EnabledVersionsForGroup returns all enabled versions for a group in order of best to worst
|
||||
func (m *APIRegistrationManager) EnabledVersionsForGroup(group string) []schema.GroupVersion {
|
||||
groupMeta, ok := m.groupMetaMap[group]
|
||||
if !ok {
|
||||
return []schema.GroupVersion{}
|
||||
}
|
||||
|
||||
ret := []schema.GroupVersion{}
|
||||
for _, version := range groupMeta.GroupVersions {
|
||||
if m.IsEnabledVersion(version) {
|
||||
ret = append(ret, version)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Group returns the metadata of a group if the group is registered, otherwise
|
||||
// an error is returned.
|
||||
func (m *APIRegistrationManager) Group(group string) (*apimachinery.GroupMeta, error) {
|
||||
groupMeta, found := m.groupMetaMap[group]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("group %v has not been registered", group)
|
||||
}
|
||||
groupMetaCopy := *groupMeta
|
||||
return &groupMetaCopy, nil
|
||||
}
|
||||
|
||||
// IsRegistered takes a string and determines if it's one of the registered groups
|
||||
func (m *APIRegistrationManager) IsRegistered(group string) bool {
|
||||
_, found := m.groupMetaMap[group]
|
||||
return found
|
||||
}
|
||||
|
||||
// IsRegisteredVersion returns if a version is registered.
|
||||
func (m *APIRegistrationManager) IsRegisteredVersion(v schema.GroupVersion) bool {
|
||||
_, found := m.registeredVersions[v]
|
||||
return found
|
||||
}
|
||||
|
||||
// RegisteredGroupVersions returns all registered group versions.
|
||||
func (m *APIRegistrationManager) RegisteredGroupVersions() []schema.GroupVersion {
|
||||
ret := []schema.GroupVersion{}
|
||||
for groupVersion := range m.registeredVersions {
|
||||
ret = append(ret, groupVersion)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// InterfacesFor is a union meta.VersionInterfacesFunc func for all registered types
|
||||
func (m *APIRegistrationManager) InterfacesFor(version schema.GroupVersion) (*meta.VersionInterfaces, error) {
|
||||
groupMeta, err := m.Group(version.Group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return groupMeta.InterfacesFor(version)
|
||||
}
|
||||
|
||||
// TODO: This is an expedient function, because we don't check if a Group is
|
||||
// supported throughout the code base. We will abandon this function and
|
||||
// checking the error returned by the Group() function.
|
||||
func (m *APIRegistrationManager) GroupOrDie(group string) *apimachinery.GroupMeta {
|
||||
groupMeta, found := m.groupMetaMap[group]
|
||||
if !found {
|
||||
if group == "" {
|
||||
panic("The legacy v1 API is not registered.")
|
||||
} else {
|
||||
panic(fmt.Sprintf("Group %s is not registered.", group))
|
||||
}
|
||||
}
|
||||
groupMetaCopy := *groupMeta
|
||||
return &groupMetaCopy
|
||||
}
|
||||
|
||||
// RESTMapper returns a union RESTMapper of all known types with priorities chosen in the following order:
|
||||
// 1. if KUBE_API_VERSIONS is specified, then KUBE_API_VERSIONS in order, OR
|
||||
// 1. legacy kube group preferred version, extensions preferred version, metrics perferred version, legacy
|
||||
// kube any version, extensions any version, metrics any version, all other groups alphabetical preferred version,
|
||||
// all other groups alphabetical.
|
||||
func (m *APIRegistrationManager) RESTMapper(versionPatterns ...schema.GroupVersion) meta.RESTMapper {
|
||||
unionMapper := meta.MultiRESTMapper{}
|
||||
unionedGroups := sets.NewString()
|
||||
for enabledVersion := range m.enabledVersions {
|
||||
if !unionedGroups.Has(enabledVersion.Group) {
|
||||
unionedGroups.Insert(enabledVersion.Group)
|
||||
groupMeta := m.groupMetaMap[enabledVersion.Group]
|
||||
unionMapper = append(unionMapper, groupMeta.RESTMapper)
|
||||
}
|
||||
}
|
||||
|
||||
if len(versionPatterns) != 0 {
|
||||
resourcePriority := []schema.GroupVersionResource{}
|
||||
kindPriority := []schema.GroupVersionKind{}
|
||||
for _, versionPriority := range versionPatterns {
|
||||
resourcePriority = append(resourcePriority, versionPriority.WithResource(meta.AnyResource))
|
||||
kindPriority = append(kindPriority, versionPriority.WithKind(meta.AnyKind))
|
||||
}
|
||||
|
||||
return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority}
|
||||
}
|
||||
|
||||
if len(m.envRequestedVersions) != 0 {
|
||||
resourcePriority := []schema.GroupVersionResource{}
|
||||
kindPriority := []schema.GroupVersionKind{}
|
||||
|
||||
for _, versionPriority := range m.envRequestedVersions {
|
||||
resourcePriority = append(resourcePriority, versionPriority.WithResource(meta.AnyResource))
|
||||
kindPriority = append(kindPriority, versionPriority.WithKind(meta.AnyKind))
|
||||
}
|
||||
|
||||
return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority}
|
||||
}
|
||||
|
||||
prioritizedGroups := []string{"", "extensions", "metrics"}
|
||||
resourcePriority, kindPriority := m.prioritiesForGroups(prioritizedGroups...)
|
||||
|
||||
prioritizedGroupsSet := sets.NewString(prioritizedGroups...)
|
||||
remainingGroups := sets.String{}
|
||||
for enabledVersion := range m.enabledVersions {
|
||||
if !prioritizedGroupsSet.Has(enabledVersion.Group) {
|
||||
remainingGroups.Insert(enabledVersion.Group)
|
||||
}
|
||||
}
|
||||
|
||||
remainingResourcePriority, remainingKindPriority := m.prioritiesForGroups(remainingGroups.List()...)
|
||||
resourcePriority = append(resourcePriority, remainingResourcePriority...)
|
||||
kindPriority = append(kindPriority, remainingKindPriority...)
|
||||
|
||||
return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority}
|
||||
}
|
||||
|
||||
// prioritiesForGroups returns the resource and kind priorities for a PriorityRESTMapper, preferring the preferred version of each group first,
|
||||
// then any non-preferred version of the group second.
|
||||
func (m *APIRegistrationManager) prioritiesForGroups(groups ...string) ([]schema.GroupVersionResource, []schema.GroupVersionKind) {
|
||||
resourcePriority := []schema.GroupVersionResource{}
|
||||
kindPriority := []schema.GroupVersionKind{}
|
||||
|
||||
for _, group := range groups {
|
||||
availableVersions := m.EnabledVersionsForGroup(group)
|
||||
if len(availableVersions) > 0 {
|
||||
resourcePriority = append(resourcePriority, availableVersions[0].WithResource(meta.AnyResource))
|
||||
kindPriority = append(kindPriority, availableVersions[0].WithKind(meta.AnyKind))
|
||||
}
|
||||
}
|
||||
for _, group := range groups {
|
||||
resourcePriority = append(resourcePriority, schema.GroupVersionResource{Group: group, Version: meta.AnyVersion, Resource: meta.AnyResource})
|
||||
kindPriority = append(kindPriority, schema.GroupVersionKind{Group: group, Version: meta.AnyVersion, Kind: meta.AnyKind})
|
||||
}
|
||||
|
||||
return resourcePriority, kindPriority
|
||||
}
|
||||
|
||||
// AllPreferredGroupVersions returns the preferred versions of all registered
|
||||
// groups in the form of "group1/version1,group2/version2,..."
|
||||
func (m *APIRegistrationManager) AllPreferredGroupVersions() string {
|
||||
if len(m.groupMetaMap) == 0 {
|
||||
return ""
|
||||
}
|
||||
var defaults []string
|
||||
for _, groupMeta := range m.groupMetaMap {
|
||||
defaults = append(defaults, groupMeta.GroupVersion.String())
|
||||
}
|
||||
sort.Strings(defaults)
|
||||
return strings.Join(defaults, ",")
|
||||
}
|
||||
|
||||
// ValidateEnvRequestedVersions returns a list of versions that are requested in
|
||||
// the KUBE_API_VERSIONS environment variable, but not enabled.
|
||||
func (m *APIRegistrationManager) ValidateEnvRequestedVersions() []schema.GroupVersion {
|
||||
var missingVersions []schema.GroupVersion
|
||||
for _, v := range m.envRequestedVersions {
|
||||
if _, found := m.enabledVersions[v]; !found {
|
||||
missingVersions = append(missingVersions, v)
|
||||
}
|
||||
}
|
||||
return missingVersions
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 apimachinery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupMeta stores the metadata of a group.
|
||||
type GroupMeta struct {
|
||||
// GroupVersion represents the preferred version of the group.
|
||||
GroupVersion schema.GroupVersion
|
||||
|
||||
// GroupVersions is Group + all versions in that group.
|
||||
GroupVersions []schema.GroupVersion
|
||||
|
||||
// SelfLinker can set or get the SelfLink field of all API types.
|
||||
// TODO: when versioning changes, make this part of each API definition.
|
||||
// TODO(lavalamp): Combine SelfLinker & ResourceVersioner interfaces, force all uses
|
||||
// to go through the InterfacesFor method below.
|
||||
SelfLinker runtime.SelfLinker
|
||||
|
||||
// RESTMapper provides the default mapping between REST paths and the objects declared in api.Scheme and all known
|
||||
// versions.
|
||||
RESTMapper meta.RESTMapper
|
||||
|
||||
// InterfacesFor returns the default Codec and ResourceVersioner for a given version
|
||||
// string, or an error if the version is not known.
|
||||
// TODO: make this stop being a func pointer and always use the default
|
||||
// function provided below once every place that populates this field has been changed.
|
||||
InterfacesFor func(version schema.GroupVersion) (*meta.VersionInterfaces, error)
|
||||
|
||||
// InterfacesByVersion stores the per-version interfaces.
|
||||
InterfacesByVersion map[schema.GroupVersion]*meta.VersionInterfaces
|
||||
}
|
||||
|
||||
// DefaultInterfacesFor returns the default Codec and ResourceVersioner for a given version
|
||||
// string, or an error if the version is not known.
|
||||
// TODO: Remove the "Default" prefix.
|
||||
func (gm *GroupMeta) DefaultInterfacesFor(version schema.GroupVersion) (*meta.VersionInterfaces, error) {
|
||||
if v, ok := gm.InterfacesByVersion[version]; ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, gm.GroupVersions)
|
||||
}
|
||||
|
||||
// AddVersionInterfaces adds the given version to the group. Only call during
|
||||
// init, after that GroupMeta objects should be immutable. Not thread safe.
|
||||
// (If you use this, be sure to set .InterfacesFor = .DefaultInterfacesFor)
|
||||
// TODO: remove the "Interfaces" suffix and make this also maintain the
|
||||
// .GroupVersions member.
|
||||
func (gm *GroupMeta) AddVersionInterfaces(version schema.GroupVersion, interfaces *meta.VersionInterfaces) error {
|
||||
if e, a := gm.GroupVersion.Group, version.Group; a != e {
|
||||
return fmt.Errorf("got a version in group %v, but am in group %v", a, e)
|
||||
}
|
||||
if gm.InterfacesByVersion == nil {
|
||||
gm.InterfacesByVersion = make(map[schema.GroupVersion]*meta.VersionInterfaces)
|
||||
}
|
||||
gm.InterfacesByVersion[version] = interfaces
|
||||
|
||||
// TODO: refactor to make the below error not possible, this function
|
||||
// should *set* GroupVersions rather than depend on it.
|
||||
for _, v := range gm.GroupVersions {
|
||||
if v == version {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("added a version interface without the corresponding version %v being in the list %#v", version, gm.GroupVersions)
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
// This file should be consistent with pkg/api/v1/annotation_key_constants.go.
|
||||
|
||||
package api
|
||||
|
||||
const (
|
||||
// ImagePolicyFailedOpenKey is added to pods created by failing open when the image policy
|
||||
// webhook backend fails.
|
||||
ImagePolicyFailedOpenKey string = "alpha.image-policy.k8s.io/failed-open"
|
||||
|
||||
// PodPresetOptOutAnnotationKey represents the annotation key for a pod to exempt itself from pod preset manipulation
|
||||
PodPresetOptOutAnnotationKey string = "podpreset.admission.kubernetes.io/exclude"
|
||||
|
||||
// MirrorAnnotationKey represents the annotation key set by kubelets when creating mirror pods
|
||||
MirrorPodAnnotationKey string = "kubernetes.io/config.mirror"
|
||||
|
||||
// TolerationsAnnotationKey represents the key of tolerations data (json serialized)
|
||||
// in the Annotations of a Pod.
|
||||
TolerationsAnnotationKey string = "scheduler.alpha.kubernetes.io/tolerations"
|
||||
|
||||
// TaintsAnnotationKey represents the key of taints data (json serialized)
|
||||
// in the Annotations of a Node.
|
||||
TaintsAnnotationKey string = "scheduler.alpha.kubernetes.io/taints"
|
||||
|
||||
// SeccompPodAnnotationKey represents the key of a seccomp profile applied
|
||||
// to all containers of a pod.
|
||||
SeccompPodAnnotationKey string = "seccomp.security.alpha.kubernetes.io/pod"
|
||||
|
||||
// SeccompContainerAnnotationKeyPrefix represents the key of a seccomp profile applied
|
||||
// to one container of a pod.
|
||||
SeccompContainerAnnotationKeyPrefix string = "container.seccomp.security.alpha.kubernetes.io/"
|
||||
|
||||
// CreatedByAnnotation represents the key used to store the spec(json)
|
||||
// used to create the resource.
|
||||
// This field is deprecated in favor of ControllerRef (see #44407).
|
||||
// TODO(#50720): Remove this field in v1.9.
|
||||
CreatedByAnnotation = "kubernetes.io/created-by"
|
||||
|
||||
// PreferAvoidPodsAnnotationKey represents the key of preferAvoidPods data (json serialized)
|
||||
// in the Annotations of a Node.
|
||||
PreferAvoidPodsAnnotationKey string = "scheduler.alpha.kubernetes.io/preferAvoidPods"
|
||||
|
||||
// SysctlsPodAnnotationKey represents the key of sysctls which are set for the infrastructure
|
||||
// container of a pod. The annotation value is a comma separated list of sysctl_name=value
|
||||
// key-value pairs. Only a limited set of whitelisted and isolated sysctls is supported by
|
||||
// the kubelet. Pods with other sysctls will fail to launch.
|
||||
SysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/sysctls"
|
||||
|
||||
// UnsafeSysctlsPodAnnotationKey represents the key of sysctls which are set for the infrastructure
|
||||
// container of a pod. The annotation value is a comma separated list of sysctl_name=value
|
||||
// key-value pairs. Unsafe sysctls must be explicitly enabled for a kubelet. They are properly
|
||||
// namespaced to a pod or a container, but their isolation is usually unclear or weak. Their use
|
||||
// is at-your-own-risk. Pods that attempt to set an unsafe sysctl that is not enabled for a kubelet
|
||||
// will fail to launch.
|
||||
UnsafeSysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/unsafe-sysctls"
|
||||
|
||||
// ObjectTTLAnnotations represents a suggestion for kubelet for how long it can cache
|
||||
// an object (e.g. secret, config map) before fetching it again from apiserver.
|
||||
// This annotation can be attached to node.
|
||||
ObjectTTLAnnotationKey string = "node.alpha.kubernetes.io/ttl"
|
||||
|
||||
// annotation key prefix used to identify non-convertible json paths.
|
||||
NonConvertibleAnnotationPrefix = "non-convertible.kubernetes.io"
|
||||
|
||||
kubectlPrefix = "kubectl.kubernetes.io/"
|
||||
|
||||
// LastAppliedConfigAnnotation is the annotation used to store the previous
|
||||
// configuration of a resource for use in a three way diff by UpdateApplyAnnotation.
|
||||
LastAppliedConfigAnnotation = kubectlPrefix + "last-applied-configuration"
|
||||
|
||||
// AnnotationLoadBalancerSourceRangesKey is the key of the annotation on a service to set allowed ingress ranges on their LoadBalancers
|
||||
//
|
||||
// It should be a comma-separated list of CIDRs, e.g. `0.0.0.0/0` to
|
||||
// allow full access (the default) or `18.0.0.0/8,56.0.0.0/8` to allow
|
||||
// access only from the CIDRs currently allocated to MIT & the USPS.
|
||||
//
|
||||
// Not all cloud providers support this annotation, though AWS & GCE do.
|
||||
AnnotationLoadBalancerSourceRangesKey = "service.beta.kubernetes.io/load-balancer-source-ranges"
|
||||
)
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package,register
|
||||
|
||||
// Package api contains the latest (or "internal") version of the
|
||||
// Kubernetes API objects. This is the API objects as represented in memory.
|
||||
// The contract presented to clients is located in the versioned packages,
|
||||
// which are sub-directories. The first one is "v1". Those packages
|
||||
// describe how a particular version is serialized to storage/network.
|
||||
package api // import "k8s.io/kubernetes/pkg/api"
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 api
|
||||
|
||||
// Field path constants that are specific to the internal API
|
||||
// representation.
|
||||
const (
|
||||
NodeUnschedulableField = "spec.unschedulable"
|
||||
ObjectNameField = "metadata.name"
|
||||
PodHostField = "spec.nodeName"
|
||||
PodStatusField = "status.phase"
|
||||
SecretTypeField = "type"
|
||||
|
||||
EventReasonField = "reason"
|
||||
EventSourceField = "source"
|
||||
EventTypeField = "type"
|
||||
EventInvolvedKindField = "involvedObject.kind"
|
||||
EventInvolvedNamespaceField = "involvedObject.namespace"
|
||||
EventInvolvedNameField = "involvedObject.name"
|
||||
EventInvolvedUIDField = "involvedObject.uid"
|
||||
EventInvolvedAPIVersionField = "involvedObject.apiVersion"
|
||||
EventInvolvedResourceVersionField = "involvedObject.resourceVersion"
|
||||
EventInvolvedFieldPathField = "involvedObject.fieldPath"
|
||||
)
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 api
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// This file implements json marshaling/unmarshaling interfaces on objects that are currently marshaled into annotations
|
||||
// to prevent anyone from marshaling these internal structs.
|
||||
|
||||
var _ = json.Marshaler(&AvoidPods{})
|
||||
var _ = json.Unmarshaler(&AvoidPods{})
|
||||
|
||||
func (AvoidPods) MarshalJSON() ([]byte, error) { panic("do not marshal internal struct") }
|
||||
func (*AvoidPods) UnmarshalJSON([]byte) error { panic("do not unmarshal to internal struct") }
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
//TODO: consider making these methods functions, because we don't want helper
|
||||
//functions in the k8s.io/api repo.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func (obj *ObjectReference) SetGroupVersionKind(gvk schema.GroupVersionKind) {
|
||||
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
||||
}
|
||||
|
||||
func (obj *ObjectReference) GroupVersionKind() schema.GroupVersionKind {
|
||||
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
|
||||
}
|
||||
|
||||
func (obj *ObjectReference) GetObjectKind() schema.ObjectKind { return obj }
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 api
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apimachinery/announced"
|
||||
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
)
|
||||
|
||||
// GroupFactoryRegistry is the APIGroupFactoryRegistry (overlaps a bit with Registry, see comments in package for details)
|
||||
var GroupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
|
||||
|
||||
// Registry is an instance of an API registry. This is an interim step to start removing the idea of a global
|
||||
// API registry.
|
||||
var Registry = registered.NewOrDie(os.Getenv("KUBE_API_VERSIONS"))
|
||||
|
||||
// Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered.
|
||||
// NOTE: If you are copying this file to start a new api group, STOP! Copy the
|
||||
// extensions group instead. This Scheme is special and should appear ONLY in
|
||||
// the api group, unless you really know what you're doing.
|
||||
// TODO(lavalamp): make the above error impossible.
|
||||
var Scheme = runtime.NewScheme()
|
||||
|
||||
// Codecs provides access to encoding and decoding for the scheme
|
||||
var Codecs = serializer.NewCodecFactory(Scheme)
|
||||
|
||||
// GroupName is the group name use in this package
|
||||
const GroupName = ""
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
|
||||
|
||||
// ParameterCodec handles versioning of objects that are converted to query parameters.
|
||||
var ParameterCodec = runtime.NewParameterCodec(Scheme)
|
||||
|
||||
// Kind takes an unqualified kind and returns a Group qualified GroupKind
|
||||
func Kind(kind string) schema.GroupKind {
|
||||
return SchemeGroupVersion.WithKind(kind).GroupKind()
|
||||
}
|
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
|
||||
var (
|
||||
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
if err := scheme.AddIgnoredConversionType(&metav1.TypeMeta{}, &metav1.TypeMeta{}); err != nil {
|
||||
return err
|
||||
}
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&Pod{},
|
||||
&PodList{},
|
||||
&PodStatusResult{},
|
||||
&PodTemplate{},
|
||||
&PodTemplateList{},
|
||||
&ReplicationControllerList{},
|
||||
&ReplicationController{},
|
||||
&ServiceList{},
|
||||
&Service{},
|
||||
&ServiceProxyOptions{},
|
||||
&NodeList{},
|
||||
&Node{},
|
||||
&NodeConfigSource{},
|
||||
&NodeProxyOptions{},
|
||||
&Endpoints{},
|
||||
&EndpointsList{},
|
||||
&Binding{},
|
||||
&Event{},
|
||||
&EventList{},
|
||||
&List{},
|
||||
&LimitRange{},
|
||||
&LimitRangeList{},
|
||||
&ResourceQuota{},
|
||||
&ResourceQuotaList{},
|
||||
&Namespace{},
|
||||
&NamespaceList{},
|
||||
&ServiceAccount{},
|
||||
&ServiceAccountList{},
|
||||
&Secret{},
|
||||
&SecretList{},
|
||||
&PersistentVolume{},
|
||||
&PersistentVolumeList{},
|
||||
&PersistentVolumeClaim{},
|
||||
&PersistentVolumeClaimList{},
|
||||
&PodAttachOptions{},
|
||||
&PodLogOptions{},
|
||||
&PodExecOptions{},
|
||||
&PodPortForwardOptions{},
|
||||
&PodProxyOptions{},
|
||||
&ComponentStatus{},
|
||||
&ComponentStatusList{},
|
||||
&SerializedReference{},
|
||||
&RangeAllocation{},
|
||||
&ConfigMap{},
|
||||
&ConfigMapList{},
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 api
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
func (self ResourceName) String() string {
|
||||
return string(self)
|
||||
}
|
||||
|
||||
// Returns the CPU limit if specified.
|
||||
func (self *ResourceList) Cpu() *resource.Quantity {
|
||||
if val, ok := (*self)[ResourceCPU]; ok {
|
||||
return &val
|
||||
}
|
||||
return &resource.Quantity{Format: resource.DecimalSI}
|
||||
}
|
||||
|
||||
// Returns the Memory limit if specified.
|
||||
func (self *ResourceList) Memory() *resource.Quantity {
|
||||
if val, ok := (*self)[ResourceMemory]; ok {
|
||||
return &val
|
||||
}
|
||||
return &resource.Quantity{Format: resource.BinarySI}
|
||||
}
|
||||
|
||||
func (self *ResourceList) Pods() *resource.Quantity {
|
||||
if val, ok := (*self)[ResourcePods]; ok {
|
||||
return &val
|
||||
}
|
||||
return &resource.Quantity{}
|
||||
}
|
||||
|
||||
func (self *ResourceList) NvidiaGPU() *resource.Quantity {
|
||||
if val, ok := (*self)[ResourceNvidiaGPU]; ok {
|
||||
return &val
|
||||
}
|
||||
return &resource.Quantity{}
|
||||
}
|
||||
|
||||
func (self *ResourceList) StorageEphemeral() *resource.Quantity {
|
||||
if val, ok := (*self)[ResourceEphemeralStorage]; ok {
|
||||
return &val
|
||||
}
|
||||
return &resource.Quantity{}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
//TODO: consider making these methods functions, because we don't want helper
|
||||
//functions in the k8s.io/api repo.
|
||||
|
||||
package api
|
||||
|
||||
import "fmt"
|
||||
|
||||
// MatchTaint checks if the taint matches taintToMatch. Taints are unique by key:effect,
|
||||
// if the two taints have same key:effect, regard as they match.
|
||||
func (t *Taint) MatchTaint(taintToMatch Taint) bool {
|
||||
return t.Key == taintToMatch.Key && t.Effect == taintToMatch.Effect
|
||||
}
|
||||
|
||||
// taint.ToString() converts taint struct to string in format key=value:effect or key:effect.
|
||||
func (t *Taint) ToString() string {
|
||||
if len(t.Value) == 0 {
|
||||
return fmt.Sprintf("%v:%v", t.Key, t.Effect)
|
||||
}
|
||||
return fmt.Sprintf("%v=%v:%v", t.Key, t.Value, t.Effect)
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
//TODO: consider making these methods functions, because we don't want helper
|
||||
//functions in the k8s.io/api repo.
|
||||
|
||||
package api
|
||||
|
||||
// MatchToleration checks if the toleration matches tolerationToMatch. Tolerations are unique by <key,effect,operator,value>,
|
||||
// if the two tolerations have same <key,effect,operator,value> combination, regard as they match.
|
||||
// TODO: uniqueness check for tolerations in api validations.
|
||||
func (t *Toleration) MatchToleration(tolerationToMatch *Toleration) bool {
|
||||
return t.Key == tolerationToMatch.Key &&
|
||||
t.Effect == tolerationToMatch.Effect &&
|
||||
t.Operator == tolerationToMatch.Operator &&
|
||||
t.Value == tolerationToMatch.Value
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue