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)
|
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
|
// Stacks returns a client for Docker's Stack on Kubernetes
|
||||||
func (s *Factory) Stacks(allNamespaces bool) (StackClient, error) {
|
func (s *Factory) Stacks(allNamespaces bool) (StackClient, error) {
|
||||||
version, err := kubernetes.GetStackAPIVersion(s.clientSet)
|
version, err := kubernetes.GetStackAPIVersion(s.clientSet)
|
||||||
|
|
|
@ -2,10 +2,13 @@ package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command/formatter"
|
"github.com/docker/cli/cli/command/formatter"
|
||||||
"github.com/docker/cli/kubernetes/labels"
|
"github.com/docker/cli/kubernetes/labels"
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
@ -65,13 +68,43 @@ func toSwarmProtocol(protocol apiv1.Protocol) swarm.PortConfigProtocol {
|
||||||
return swarm.PortConfigProtocol("unknown")
|
return swarm.PortConfigProtocol("unknown")
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchPods(namespace string, pods corev1.PodInterface) ([]apiv1.Pod, error) {
|
func fetchPods(stackName string, pods corev1.PodInterface, f filters.Args) ([]apiv1.Pod, error) {
|
||||||
labelSelector := labels.SelectorForStack(namespace)
|
services := f.Get("service")
|
||||||
podsList, err := pods.List(metav1.ListOptions{LabelSelector: labelSelector})
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
func getContainerImage(containers []apiv1.Container) string {
|
||||||
|
@ -121,56 +154,76 @@ const (
|
||||||
publishedOnRandomPortSuffix = "-random-ports"
|
publishedOnRandomPortSuffix = "-random-ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Replicas conversion
|
func convertToServices(replicas *appsv1beta2.ReplicaSetList, daemons *appsv1beta2.DaemonSetList, services *apiv1.ServiceList) ([]swarm.Service, map[string]formatter.ServiceListInfo, error) {
|
||||||
func replicasToServices(replicas *appsv1beta2.ReplicaSetList, services *apiv1.ServiceList) ([]swarm.Service, map[string]formatter.ServiceListInfo, error) {
|
|
||||||
result := make([]swarm.Service, len(replicas.Items))
|
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 {
|
for i, r := range replicas.Items {
|
||||||
serviceName := r.Labels[labels.ForServiceName]
|
s, err := convertToService(r.Labels[labels.ForServiceName], services, r.Spec.Template.Spec.Containers)
|
||||||
serviceHeadless, ok := findService(services, serviceName)
|
if err != nil {
|
||||||
if !ok {
|
return nil, nil, err
|
||||||
return nil, nil, fmt.Errorf("could not find service '%s'", serviceName)
|
|
||||||
}
|
}
|
||||||
stack, ok := serviceHeadless.Labels[labels.ForStackName]
|
result[i] = *s
|
||||||
if ok {
|
infos[s.ID] = formatter.ServiceListInfo{
|
||||||
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{
|
|
||||||
Mode: "replicated",
|
Mode: "replicated",
|
||||||
Replicas: fmt.Sprintf("%d/%d", r.Status.AvailableReplicas, r.Status.Replicas),
|
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
|
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 {
|
for _, s := range services.Items {
|
||||||
if s.Name == name {
|
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 {
|
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)},
|
Items: []appsv1beta2.ReplicaSet{makeReplicaSet("unknown", 0, 0)},
|
||||||
}
|
}
|
||||||
services := apiv1.ServiceList{}
|
services := apiv1.ServiceList{}
|
||||||
_, _, err := replicasToServices(&replicas, &services)
|
_, _, err := convertToServices(&replicas, &appsv1beta2.DaemonSetList{}, &services)
|
||||||
assert.ErrorContains(t, err, "could not find service")
|
assert.ErrorContains(t, err, "could not find service")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ func TestKubernetesServiceToSwarmServiceConversion(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
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.NilError(t, err)
|
||||||
assert.DeepEqual(t, tc.expectedServices, swarmServices)
|
assert.DeepEqual(t, tc.expectedServices, swarmServices)
|
||||||
assert.DeepEqual(t, tc.expectedListInfo, listInfo)
|
assert.DeepEqual(t, tc.expectedListInfo, listInfo)
|
||||||
|
|
|
@ -10,17 +10,23 @@ import (
|
||||||
"github.com/docker/cli/cli/command/task"
|
"github.com/docker/cli/cli/command/task"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
|
||||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
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
|
// RunPS is the kubernetes implementation of docker stack ps
|
||||||
func RunPS(dockerCli *KubeCli, options options.PS) error {
|
func RunPS(dockerCli *KubeCli, options options.PS) error {
|
||||||
namespace := options.Namespace
|
filters := options.Filter.Value()
|
||||||
|
if err := filters.Validate(supportedPSFilters); err != nil {
|
||||||
// Initialize clients
|
return err
|
||||||
|
}
|
||||||
client, err := dockerCli.composeClient()
|
client, err := dockerCli.composeClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -29,78 +35,71 @@ func RunPS(dockerCli *KubeCli, options options.PS) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
podsClient := client.Pods()
|
stackName := options.Namespace
|
||||||
|
_, err = stacks.Get(stackName)
|
||||||
// Fetch pods
|
if apierrs.IsNotFound(err) {
|
||||||
if _, err := stacks.Get(namespace); err != nil {
|
return fmt.Errorf("nothing found in stack: %s", stackName)
|
||||||
return fmt.Errorf("nothing found in stack: %s", namespace)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pods, err := fetchPods(namespace, podsClient)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
pods, err := fetchPods(stackName, client.Pods(), filters)
|
||||||
if len(pods) == 0 {
|
if err != nil {
|
||||||
return fmt.Errorf("nothing found in stack: %s", namespace)
|
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
|
format := options.Format
|
||||||
if len(format) == 0 {
|
if format == "" {
|
||||||
format = task.DefaultFormat(dockerCli.ConfigFile(), options.Quiet)
|
format = task.DefaultFormat(dockerCli.ConfigFile(), options.Quiet)
|
||||||
}
|
}
|
||||||
nodeResolver := makeNodeResolver(options.NoResolve, client.Nodes())
|
|
||||||
|
|
||||||
tasks := make([]swarm.Task, len(pods))
|
tasks := make([]swarm.Task, len(pods))
|
||||||
for i, pod := range pods {
|
for i, pod := range pods {
|
||||||
tasks[i] = podToTask(pod)
|
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))
|
sort.Stable(tasksBySlot(tasks))
|
||||||
|
|
||||||
names := map[string]string{}
|
names := map[string]string{}
|
||||||
nodes := map[string]string{}
|
nodes := map[string]string{}
|
||||||
|
|
||||||
tasksCtx := formatter.Context{
|
n, err := client.Nodes().List(metav1.ListOptions{})
|
||||||
Output: dockerCli.Out(),
|
if err != nil {
|
||||||
Format: formatter.NewTaskFormat(format, quiet),
|
return err
|
||||||
Trunc: trunc,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, task := range tasks {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
names[task.ID] = fmt.Sprintf("%s_%s", namespace, pods[i].Name)
|
names[task.ID] = fmt.Sprintf("%s_%s", namespace, pods[i].Name)
|
||||||
nodes[task.ID] = nodeValue
|
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)
|
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
|
// 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 {
|
if noResolve {
|
||||||
return func(name string) (string, error) {
|
for _, node := range nodes.Items {
|
||||||
n, err := nodes.List(metav1.ListOptions{
|
if node.Name == name {
|
||||||
FieldSelector: fields.OneTermEqualSelector(api.ObjectNameField, name).String(),
|
return string(node.UID), nil
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command/formatter"
|
"github.com/docker/cli/cli/command/formatter"
|
||||||
"github.com/docker/cli/cli/command/stack/options"
|
"github.com/docker/cli/cli/command/stack/options"
|
||||||
"github.com/docker/cli/kubernetes/labels"
|
"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"
|
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
|
// RunServices is the kubernetes implementation of docker stack services
|
||||||
func RunServices(dockerCli *KubeCli, opts options.Services) error {
|
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()
|
client, err := dockerCli.composeClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
stacks, err := client.Stacks(false)
|
stacks, err := client.Stacks(false)
|
||||||
if err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
stackName := opts.Namespace
|
||||||
replicasList, err := replicas.List(metav1.ListOptions{LabelSelector: labels.SelectorForStack(opts.Namespace)})
|
_, err = stacks.Get(stackName)
|
||||||
|
if apierrs.IsNotFound(err) {
|
||||||
|
return fmt.Errorf("nothing found in stack: %s", stackName)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Replicas sets and kubernetes services to swam services and formatter informations
|
// 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 {
|
if err != nil {
|
||||||
return err
|
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.NoTrunc, "no-trunc", false, "Do not truncate output")
|
||||||
flags.BoolVar(&opts.NoResolve, "no-resolve", false, "Do not map IDs to Names")
|
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.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.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display task IDs")
|
||||||
flags.StringVar(&opts.Format, "format", "", "Pretty-print tasks using a Go template")
|
flags.StringVar(&opts.Format, "format", "", "Pretty-print tasks using a Go template")
|
||||||
kubernetes.AddNamespaceFlag(flags)
|
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.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")
|
||||||
flags.StringVar(&opts.Format, "format", "", "Pretty-print services using a Go template")
|
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.VarP(&opts.Filter, "filter", "f", "Filter output based on conditions provided")
|
||||||
flags.SetAnnotation("filter", "swarm", nil)
|
|
||||||
kubernetes.AddNamespaceFlag(flags)
|
kubernetes.AddNamespaceFlag(flags)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,8 +64,23 @@ dn7m7nhhfb9y myapp_db 1/1 mysql@sha256:a9a5b559f8821fe73d58c3606c8
|
||||||
The currently supported filters are:
|
The currently supported filters are:
|
||||||
|
|
||||||
* id / ID (`--filter id=7be5ei6sqeye`, or `--filter ID=7be5ei6sqeye`)
|
* id / ID (`--filter id=7be5ei6sqeye`, or `--filter ID=7be5ei6sqeye`)
|
||||||
* name (`--filter name=myapp_web`)
|
* Swarm: supported
|
||||||
|
* Kubernetes: not supported
|
||||||
* label (`--filter label=key=value`)
|
* 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
|
### 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