diff --git a/Gopkg.lock b/Gopkg.lock index 2785b25d27..85cf583660 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -302,6 +302,26 @@ pruneopts = "UT" revision = "f51c12702a4d776e4c1fa9b0fabab841babae631" +[[projects]] + branch = "master" + digest = "1:797d26136f04e8b3731c21e7ed71cd881d95325f46d81f8a6bc24b1c80cb0e8a" + name = "golang.org/x/tools" + packages = [ + "go/ast/astutil", + "go/gcexportdata", + "go/internal/gcimporter", + "go/internal/packagesdriver", + "go/packages", + "go/types/typeutil", + "imports", + "internal/fastwalk", + "internal/gopathwalk", + "internal/module", + "internal/semver", + ] + pruneopts = "UT" + revision = "0fdf0c73855bae8482c5d3907a9e06f33ff70a10" + [[projects]] digest = "1:08206298775e5b462e6c0333f4471b44e63f1a70e42952b6ede4ecc9572281eb" name = "google.golang.org/appengine" @@ -529,6 +549,59 @@ revision = "1638f8970cefaa404ff3a62950f88b08292b2696" version = "v9.0.0" +[[projects]] + digest = "1:956680ced4f44452e3865c5ce920e5b6292056d0977d024a1d72aad59ce36d36" + name = "k8s.io/code-generator" + packages = [ + "cmd/client-gen", + "cmd/client-gen/args", + "cmd/client-gen/generators", + "cmd/client-gen/generators/fake", + "cmd/client-gen/generators/scheme", + "cmd/client-gen/generators/util", + "cmd/client-gen/path", + "cmd/client-gen/types", + "cmd/deepcopy-gen", + "cmd/deepcopy-gen/args", + "cmd/defaulter-gen", + "cmd/defaulter-gen/args", + "cmd/informer-gen", + "cmd/informer-gen/args", + "cmd/informer-gen/generators", + "cmd/lister-gen", + "cmd/lister-gen/args", + "cmd/lister-gen/generators", + "pkg/util", + ] + pruneopts = "UT" + revision = "3dcf91f64f638563e5106f21f50c31fa361c918d" + version = "kubernetes-1.12.2" + +[[projects]] + branch = "master" + digest = "1:39912eb5f8eaf46486faae0839586c27c93423e552f76875defa048f52c15c15" + name = "k8s.io/gengo" + packages = [ + "args", + "examples/deepcopy-gen/generators", + "examples/defaulter-gen/generators", + "examples/set-gen/sets", + "generator", + "namer", + "parser", + "types", + ] + pruneopts = "UT" + revision = "e17681d19d3ac4837a019ece36c2a0ec31ffe985" + +[[projects]] + digest = "1:72fd56341405f53c745377e0ebc4abeff87f1a048e0eea6568a20212650f5a82" + name = "k8s.io/klog" + packages = ["."] + pruneopts = "UT" + revision = "71442cd4037d612096940ceb0f3fec3f7fff66e0" + version = "v0.2.0" + [[projects]] digest = "1:7d46c560bdf2e3d01268acf4f376480124d40718bebd4e7ed8f1eedc372480cf" name = "k8s.io/kube-openapi" @@ -567,6 +640,11 @@ "k8s.io/client-go/tools/leaderelection/resourcelock", "k8s.io/client-go/tools/record", "k8s.io/client-go/util/workqueue", + "k8s.io/code-generator/cmd/client-gen", + "k8s.io/code-generator/cmd/deepcopy-gen", + "k8s.io/code-generator/cmd/defaulter-gen", + "k8s.io/code-generator/cmd/informer-gen", + "k8s.io/code-generator/cmd/lister-gen", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 1a2678c1df..c56916ab28 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,3 +1,11 @@ +required = [ + "k8s.io/code-generator/cmd/client-gen", + "k8s.io/code-generator/cmd/informer-gen", + "k8s.io/code-generator/cmd/lister-gen", + "k8s.io/code-generator/cmd/deepcopy-gen", + "k8s.io/code-generator/cmd/defaulter-gen", +] + [[constraint]] name = "k8s.io/api" version = "kubernetes-1.12.2" @@ -6,6 +14,10 @@ name = "k8s.io/apimachinery" version = "kubernetes-1.12.2" +[[constraint]] + name = "k8s.io/code-generator" + version = "kubernetes-1.12.2" + [[constraint]] name = "k8s.io/client-go" version = "9.0.0" diff --git a/build/Dockerfile b/build/Dockerfile index 4c573fec03..6e8142bfa0 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -6,7 +6,7 @@ RUN ln -sf /proc/1/fd/1 /var/log/nginx/access.log \ && ln -sf /proc/1/fd/1 /var/log/nginx/stream-access.log \ && ln -sf /proc/1/fd/2 /var/log/nginx/error.log -COPY nginx-ingress internal/configs/version1/nginx.ingress.tmpl internal/configs/version1/nginx.tmpl / +COPY nginx-ingress internal/configs/version1/nginx.ingress.tmpl internal/configs/version1/nginx.tmpl internal/configs/version2/nginx.virtualserver.tmpl / RUN rm /etc/nginx/conf.d/* diff --git a/build/DockerfileForAlpine b/build/DockerfileForAlpine index caf0131ea9..c685a6ea88 100644 --- a/build/DockerfileForAlpine +++ b/build/DockerfileForAlpine @@ -6,7 +6,7 @@ RUN ln -sf /proc/1/fd/1 /var/log/nginx/access.log \ && ln -sf /proc/1/fd/1 /var/log/nginx/stream-access.log \ && ln -sf /proc/1/fd/2 /var/log/nginx/error.log -COPY nginx-ingress internal/configs/version1/nginx.ingress.tmpl internal/configs/version1/nginx.tmpl / +COPY nginx-ingress internal/configs/version1/nginx.ingress.tmpl internal/configs/version1/nginx.tmpl internal/configs/version2/nginx.virtualserver.tmpl / RUN rm /etc/nginx/conf.d/* diff --git a/build/DockerfileForPlus b/build/DockerfileForPlus index 49a22478ee..1fe73e2896 100644 --- a/build/DockerfileForPlus +++ b/build/DockerfileForPlus @@ -52,7 +52,7 @@ RUN ln -sf /proc/1/fd/1 /var/log/nginx/access.log \ EXPOSE 80 443 -COPY nginx-ingress internal/configs/version1/nginx-plus.ingress.tmpl internal/configs/version1/nginx-plus.tmpl / +COPY nginx-ingress internal/configs/version1/nginx-plus.ingress.tmpl internal/configs/version1/nginx-plus.tmpl internal/configs/version2/nginx-plus.virtualserver.tmpl / RUN rm /etc/nginx/conf.d/* \ && mkdir -p /etc/nginx/secrets diff --git a/cmd/nginx-ingress/main.go b/cmd/nginx-ingress/main.go index 70bb1f684b..e42d0ca78a 100644 --- a/cmd/nginx-ingress/main.go +++ b/cmd/nginx-ingress/main.go @@ -12,6 +12,7 @@ import ( "syscall" "time" + "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" "github.com/nginxinc/kubernetes-ingress/internal/metrics/collectors" "github.com/golang/glog" @@ -20,11 +21,14 @@ import ( "github.com/nginxinc/kubernetes-ingress/internal/k8s" "github.com/nginxinc/kubernetes-ingress/internal/metrics" "github.com/nginxinc/kubernetes-ingress/internal/nginx" + k8s_nginx "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned" + conf_scheme "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned/scheme" "github.com/nginxinc/nginx-plus-go-sdk/client" "github.com/prometheus/client_golang/prometheus" api_v1 "k8s.io/api/core/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" @@ -77,6 +81,10 @@ var ( `Path to the ingress NGINX configuration template for an ingress resource. (default for NGINX "nginx.ingress.tmpl"; default for NGINX Plus "nginx-plus.ingress.tmpl")`) + virtualServerTemplatePath = flag.String("virtualserver-template-path", "", + `Path to the VirtualServer NGINX configuration template for a VirtualServer resource. + (default for NGINX "nginx.virtualserver.tmpl"; default for NGINX Plus "nginx-plus.virtualserver.tmpl")`) + externalService = flag.String("external-service", "", `Specifies the name of the service with the type LoadBalancer through which the Ingress controller pods are exposed externally. The external address of the service is used when reporting the status of Ingress resources. Requires -report-ingress-status.`) @@ -111,6 +119,9 @@ The external address of the service is used when reporting the status of Ingress prometheusMetricsListenPort = flag.Int("prometheus-metrics-listen-port", 9113, "Set the port where the Prometheus metrics are exposed. [1023 - 65535]") + + enableCustomResources = flag.Bool("enable-custom-resources", false, + "Enable custom resources") ) func main() { @@ -166,11 +177,27 @@ func main() { glog.Fatalf("Failed to create client: %v.", err) } + var confClient k8s_nginx.Interface + if *enableCustomResources { + confClient, err = k8s_nginx.NewForConfig(config) + if err != nil { + glog.Fatalf("Failed to create a conf client: %v", err) + } + + // required for emitting Events for VirtualServer + err = conf_scheme.AddToScheme(scheme.Scheme) + if err != nil { + glog.Fatalf("Failed to add configuration types to the scheme: %v", err) + } + } + nginxConfTemplatePath := "nginx.tmpl" nginxIngressTemplatePath := "nginx.ingress.tmpl" + nginxVirtualServerTemplatePath := "nginx.virtualserver.tmpl" if *nginxPlus { nginxConfTemplatePath = "nginx-plus.tmpl" nginxIngressTemplatePath = "nginx-plus.ingress.tmpl" + nginxVirtualServerTemplatePath = "nginx-plus.virtualserver.tmpl" } if *mainTemplatePath != "" { @@ -179,6 +206,9 @@ func main() { if *ingressTemplatePath != "" { nginxIngressTemplatePath = *ingressTemplatePath } + if *virtualServerTemplatePath != "" { + nginxVirtualServerTemplatePath = *virtualServerTemplatePath + } nginxBinaryPath := "/usr/sbin/nginx" if *nginxDebug { @@ -190,6 +220,11 @@ func main() { glog.Fatalf("Error creating TemplateExecutor: %v", err) } + templateExecutorV2, err := version2.NewTemplateExecutor(nginxVirtualServerTemplatePath) + if err != nil { + glog.Fatalf("Error cresting TemplateExecutorV2: %v", err) + } + var registry *prometheus.Registry var managerCollector collectors.ManagerCollector var controllerCollector collectors.ControllerCollector @@ -321,26 +356,28 @@ func main() { } isWildcardEnabled := *wildcardTLSSecret != "" - cnf := configs.NewConfigurator(nginxManager, staticCfgParams, cfgParams, templateExecutor, *nginxPlus, isWildcardEnabled) + cnf := configs.NewConfigurator(nginxManager, staticCfgParams, cfgParams, templateExecutor, templateExecutorV2, *nginxPlus, isWildcardEnabled) controllerNamespace := os.Getenv("POD_NAMESPACE") lbcInput := k8s.NewLoadBalancerControllerInput{ - KubeClient: kubeClient, - ResyncPeriod: 30 * time.Second, - Namespace: *watchNamespace, - NginxConfigurator: cnf, - DefaultServerSecret: *defaultServerSecret, - IsNginxPlus: *nginxPlus, - IngressClass: *ingressClass, - UseIngressClassOnly: *useIngressClassOnly, - ExternalServiceName: *externalService, - ControllerNamespace: controllerNamespace, - ReportIngressStatus: *reportIngressStatus, - IsLeaderElectionEnabled: *leaderElectionEnabled, - LeaderElectionLockName: *leaderElectionLockName, - WildcardTLSSecret: *wildcardTLSSecret, - ConfigMaps: *nginxConfigMaps, - MetricsCollector: controllerCollector, + KubeClient: kubeClient, + ConfClient: confClient, + ResyncPeriod: 30 * time.Second, + Namespace: *watchNamespace, + NginxConfigurator: cnf, + DefaultServerSecret: *defaultServerSecret, + IsNginxPlus: *nginxPlus, + IngressClass: *ingressClass, + UseIngressClassOnly: *useIngressClassOnly, + ExternalServiceName: *externalService, + ControllerNamespace: controllerNamespace, + ReportIngressStatus: *reportIngressStatus, + IsLeaderElectionEnabled: *leaderElectionEnabled, + LeaderElectionLockName: *leaderElectionLockName, + WildcardTLSSecret: *wildcardTLSSecret, + ConfigMaps: *nginxConfigMaps, + AreCustomResourcesEnabled: *enableCustomResources, + MetricsCollector: controllerCollector, } lbc := k8s.NewLoadBalancerController(lbcInput) diff --git a/deployments/common/custom-resource-definitions.yaml b/deployments/common/custom-resource-definitions.yaml new file mode 100644 index 0000000000..3e3d83df80 --- /dev/null +++ b/deployments/common/custom-resource-definitions.yaml @@ -0,0 +1,35 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: virtualservers.k8s.nginx.org +spec: + group: k8s.nginx.org + versions: + - name: v1alpha1 + served: true + storage: true + scope: Namespaced + names: + plural: virtualservers + singular: virtualserver + kind: VirtualServer + shortNames: + - vs +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: virtualserverroutes.k8s.nginx.org +spec: + group: k8s.nginx.org + versions: + - name: v1alpha1 + served: true + storage: true + scope: Namespaced + names: + plural: virtualserverroutes + singular: virtualserverroute + kind: VirtualServerRoute + shortNames: + - vsr diff --git a/deployments/daemon-set/nginx-ingress.yaml b/deployments/daemon-set/nginx-ingress.yaml index 58feb124ae..2c7283d9df 100644 --- a/deployments/daemon-set/nginx-ingress.yaml +++ b/deployments/daemon-set/nginx-ingress.yaml @@ -46,3 +46,4 @@ spec: #- -external-service=nginx-ingress #- -enable-leader-election #- -enable-prometheus-metrics + #- -enable-custom-resources diff --git a/deployments/daemon-set/nginx-plus-ingress.yaml b/deployments/daemon-set/nginx-plus-ingress.yaml index 4a66b8b12a..9afab5d538 100644 --- a/deployments/daemon-set/nginx-plus-ingress.yaml +++ b/deployments/daemon-set/nginx-plus-ingress.yaml @@ -47,3 +47,4 @@ spec: #- -external-service=nginx-ingress #- -enable-leader-election #- -enable-prometheus-metrics + #- -enable-custom-resources diff --git a/deployments/deployment/nginx-ingress.yaml b/deployments/deployment/nginx-ingress.yaml index 9843b73948..4963c55d4b 100644 --- a/deployments/deployment/nginx-ingress.yaml +++ b/deployments/deployment/nginx-ingress.yaml @@ -45,3 +45,4 @@ spec: #- -external-service=nginx-ingress #- -enable-leader-election #- -enable-prometheus-metrics + #- -enable-custom-resources diff --git a/deployments/deployment/nginx-plus-ingress.yaml b/deployments/deployment/nginx-plus-ingress.yaml index f5683a7364..40334c4b72 100644 --- a/deployments/deployment/nginx-plus-ingress.yaml +++ b/deployments/deployment/nginx-plus-ingress.yaml @@ -46,3 +46,4 @@ spec: #- -external-service=nginx-ingress #- -enable-leader-election #- -enable-prometheus-metrics + #- -enable-custom-resources diff --git a/deployments/helm-chart/Chart.yaml b/deployments/helm-chart/Chart.yaml index be50fb392b..2f6b99c7c3 100644 --- a/deployments/helm-chart/Chart.yaml +++ b/deployments/helm-chart/Chart.yaml @@ -1,5 +1,5 @@ name: nginx-ingress -version: 0.6.0 +version: 0.7.0 appVersion: edge description: NGINX Ingress Controller icon: https://raw.githubusercontent.com/nginxinc/kubernetes-ingress/master/deployments/helm-chart/chart-icon.png diff --git a/deployments/helm-chart/README.md b/deployments/helm-chart/README.md index 6e9e44a5e3..7933f9085a 100644 --- a/deployments/helm-chart/README.md +++ b/deployments/helm-chart/README.md @@ -81,6 +81,7 @@ Parameter | Description | Default `controller.ingressClass` | A class of the Ingress controller. The Ingress controller only processes Ingress resources that belong to its class - i.e. have the annotation `"kubernetes.io/ingress.class"` equal to the class. Additionally, the Ingress controller processes Ingress resources that do not have that annotation which can be disabled by setting the "-use-ingress-class-only" flag. | nginx `controller.useIngressClassOnly` | Ignore Ingress resources without the `"kubernetes.io/ingress.class"` annotation. | false `controller.watchNamespace` | Namespace to watch for Ingress resources. By default the Ingress controller watches all namespaces. | "" +`controller.enableCustomResources` | Enable the custom resources. | false `controller.healthStatus` | Add a location "/nginx-health" to the default server. The location responds with the 200 status code for any request. Useful for external health-checking of the Ingress controller. | false `controller.nginxStatus.enable` | Enable the NGINX stub_status, or the NGINX Plus API. | true `controller.nginxStatus.port` | Set the port where the NGINX stub_status or the NGINX Plus API is exposed. | 8080 diff --git a/deployments/helm-chart/templates/controller-custom-resources.yaml b/deployments/helm-chart/templates/controller-custom-resources.yaml new file mode 100644 index 0000000000..3b371e7aa5 --- /dev/null +++ b/deployments/helm-chart/templates/controller-custom-resources.yaml @@ -0,0 +1,41 @@ +{{- if .Values.controller.enableCustomResources }} +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: virtualservers.k8s.nginx.org + labels: + {{- include "nginx-ingress.labels" . | nindent 4 }} +spec: + group: k8s.nginx.org + versions: + - name: v1alpha1 + served: true + storage: true + scope: Namespaced + names: + plural: virtualservers + singular: virtualserver + kind: VirtualServer + shortNames: + - vs +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: virtualserverroutes.k8s.nginx.org + labels: + {{- include "nginx-ingress.labels" . | nindent 4 }} +spec: + group: k8s.nginx.org + versions: + - name: v1alpha1 + served: true + storage: true + scope: Namespaced + names: + plural: virtualserverroutes + singular: virtualserverroute + kind: VirtualServerRoute + shortNames: + - vsr +{{- end }} \ No newline at end of file diff --git a/deployments/helm-chart/templates/controller-daemonset.yaml b/deployments/helm-chart/templates/controller-daemonset.yaml index 628a93c47e..611d7ff8cd 100644 --- a/deployments/helm-chart/templates/controller-daemonset.yaml +++ b/deployments/helm-chart/templates/controller-daemonset.yaml @@ -101,4 +101,5 @@ spec: {{- end }} - -enable-prometheus-metrics={{ .Values.prometheus.create }} - -prometheus-metrics-listen-port={{ .Values.prometheus.port }} + - -enable-custom-resources={{ .Values.controller.enableCustomResources }} {{- end }} diff --git a/deployments/helm-chart/templates/controller-deployment.yaml b/deployments/helm-chart/templates/controller-deployment.yaml index 2a1097628b..1bb574c6da 100644 --- a/deployments/helm-chart/templates/controller-deployment.yaml +++ b/deployments/helm-chart/templates/controller-deployment.yaml @@ -99,4 +99,5 @@ spec: {{- end }} - -enable-prometheus-metrics={{ .Values.prometheus.create }} - -prometheus-metrics-listen-port={{ .Values.prometheus.port }} + - -enable-custom-resources={{ .Values.controller.enableCustomResources }} {{- end }} diff --git a/deployments/helm-chart/templates/rbac.yaml b/deployments/helm-chart/templates/rbac.yaml index 9a26c6056f..1646184888 100644 --- a/deployments/helm-chart/templates/rbac.yaml +++ b/deployments/helm-chart/templates/rbac.yaml @@ -64,6 +64,17 @@ rules: verbs: - update {{- end }} +{{- if .Values.controller.enableCustomResources }} +- apiGroups: + - k8s.nginx.org + resources: + - virtualservers + - virtualserverroutes + verbs: + - list + - watch + - get +{{- end }} --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 diff --git a/deployments/helm-chart/values.yaml b/deployments/helm-chart/values.yaml index ede0e80631..e65edf8e13 100644 --- a/deployments/helm-chart/values.yaml +++ b/deployments/helm-chart/values.yaml @@ -88,6 +88,9 @@ controller: ## Namespace to watch for Ingress resources. By default the Ingress controller watches all namespaces. watchNamespace: "" + ## Enable the custom resources. + enableCustomResources: false + ## Add a location "/nginx-health" to the default server. The location responds with the 200 status code for any request. ## Useful for external health-checking of the Ingress controller. healthStatus: false diff --git a/deployments/rbac/rbac.yaml b/deployments/rbac/rbac.yaml index 1a4ff5d432..c333b6df20 100644 --- a/deployments/rbac/rbac.yaml +++ b/deployments/rbac/rbac.yaml @@ -57,6 +57,15 @@ rules: - ingresses/status verbs: - update +- apiGroups: + - k8s.nginx.org + resources: + - virtualservers + - virtualserverroutes + verbs: + - list + - watch + - get --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 diff --git a/docs/cli-arguments.md b/docs/cli-arguments.md index ea2626fd1c..2339ae2e55 100644 --- a/docs/cli-arguments.md +++ b/docs/cli-arguments.md @@ -13,6 +13,8 @@ Usage of ./nginx-ingress: A Secret with a TLS certificate and key for TLS termination of every Ingress host for which TLS termination is enabled but the Secret is not specified. Format: /. If the argument is not set, for such Ingress hosts NGINX will break any attempt to establish a TLS connection. If the argument is set, but the Ingress controller is not able to fetch the Secret from Kubernetes API, the Ingress controller will fail to start. + -enable-custom-resources + Enable custom resources -enable-leader-election Enable Leader election to avoid multiple replicas of the controller reporting the status of Ingress resources -- only one replica will report status. See -report-ingress-status flag. -external-service string @@ -67,6 +69,9 @@ Usage of ./nginx-ingress: log level for V logs -version Print the version and git-commit hash and exit + -virtualserver-template-path string + Path to the VirtualServer NGINX configuration template for a VirtualServer resource. + (default for NGINX "nginx.virtualserver.tmpl"; default for NGINX Plus "nginx-plus.virtualserver.tmpl") -vmodule value comma-separated list of pattern=N settings for file-filtered logging -watch-namespace string diff --git a/docs/installation.md b/docs/installation.md index bcdaefe8ff..f757143ef5 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -9,7 +9,7 @@ Make sure you have access to the Ingress controller image: The installation manifests are located in the [deployments](../deployments) folder. In the steps below we assume that you will be running the commands from that folder. -## 1. Create a Namespace, a SA, the Default Secret and the Customization Config Map. +## 1. Create a Namespace, a SA, the Default Secret, the Customization Config Map, and Custom Resource Definitions 1. Create a namespace and a service account for the Ingress controller: ``` @@ -28,6 +28,12 @@ The installation manifests are located in the [deployments](../deployments) fold $ kubectl apply -f common/nginx-config.yaml ``` +1. (Optional) To use the [VirtualServer and VirtualServerRoute](virtualserver-and-virtualserverroute.md) resources, create the corresponding resource definitions: + ``` + $ kubectl apply -f common/custom-resource-definitions.yaml + ``` + Note: in Step 3, make sure the Ingress controller starts with the `-enable-custom-resources` [command-line argument](cli-arguments.md). + ## 2. Configure RBAC If RBAC is enabled in your cluster, create a cluster role and bind it to the service account, created in Step 1: diff --git a/docs/virtualserver-and-virtualserverroute.md b/docs/virtualserver-and-virtualserverroute.md new file mode 100644 index 0000000000..40d3adff56 --- /dev/null +++ b/docs/virtualserver-and-virtualserverroute.md @@ -0,0 +1,334 @@ +# VirtualServer and VirtualServerRoute Resources + +The VirtualServer and VirtualServerRoute resources are new load balancing configuration, introduced in release 1.5 as an alternative to the Ingress resource. The resources enable use cases not supported with the Ingress resource, such as traffic splitting and advanced content-based routing. The resources are implemented as [Custom Resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/). + +This document is the reference documentation for the resources. To see additional examples of using the resources for specific use cases, go to the [examples-of-custom-resources](../examples-of-custom-resources) folder. + +**Feature Status**: The VirtualServer and VirtualServerRoute resources are available as a preview feature: it is suitable for experimenting and testing; however, it must be used with caution in production environments. Additionally, while the feature is in preview, we might introduce some backward-incompatible changes to the resources specification in the next releases. + +## Contents +* [Prerequisites](#Prerequisites) +* [VirtualServer Specification](#VirtualServer-Specification) +* [VirtualServerRoute Specification](#VirtualServerRoute-Specification) +* [Using VirtualServer and VirtualServerRoute](#Using-VirtualServer-and-VirtualServerRoute) +* [Customization via ConfigMap](#Customization-via-ConfigMap) + +## Prerequisites + +The VirtualServer and VirtualServerRoute resources are disabled by default. Make sure to follow Step 1.4 of the [installation](installation.md) doc during the installation process to enable the resources. + +## VirtualServer Specification + +The VirtualServer resource defines load balancing configuration for a domain name, such as `example.com`. Below is an example of such configuration: +```yaml +apiVersion: k8s.nginx.org/v1alpha1 +kind: VirtualServer +metadata: + name: cafe +spec: + host: cafe.example.com + tls: + secret: cafe-secret + upstreams: + - name: tea + service: tea-svc + port: 80 + - name: coffee + service: coffee-svc + port: 80 + routes: + - path: /tea + upstream: tea + - path: /coffee + upstream: coffee +``` + +| Field | Description | Type | Required | +| ----- | ----------- | ---- | -------- | +| `host` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as `my-app` or `hello.example.com`. Wildcard domains like `*.example.com` are not allowed. | `string` | Yes | +| `tls` | The TLS termination configuration. | [`tls`](#VirtualServerTLS) | No | +| `upstreams` | A list of upstreams. | [`[]upstream`](#Upstream) | No | +| `routes` | A list of routes. | [`[]route`](#VirtualServerRoute) | No | + +### VirtualServer.TLS + +The tls field defines TLS configuration for a VirtualServer. For example: +```yaml +secret: cafe-secret +``` + +| Field | Description | Type | Required | +| ----- | ----------- | ---- | -------- | +| `secret` | The name of a secret with a TLS certificate and key. The secret must belong to the same namespace as the VirtualServer. The secret must contain keys named `tls.crt` and `tls.key` that contain the certificate and private key as described [here](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls). If the secret doesn't exist, NGINX will break any attempt to establish a TLS connection to the host of the VirtualServer. | `string` | Yes | + + +### VirtualServer.Route + +The route defines rules for routing requests to one or multiple upstreams. For example: +```yaml +path: /tea +upstream: tea +``` + +| Field | Description | Type | Required | +| ----- | ----------- | ---- | -------- | +| `path` | The path of the route. NGINX will match it against the URI of a request. The path must start with `/` and must not include any whitespace characters, `{`, `}` or `;`. For example, `/`, `/path` are valid. The path must be unique among the paths of all routes of the VirtualServer. | `string` | Yes | +| `upstream` | The name of an upstream. The upstream with that name must be defined in the VirtualServer. | `string` | No* | +| `splits` | The splits configuration for traffic splitting. Must include at least 2 splits. | [`[]split`](#Split) | No* | +| `rules` | The rules configuration for advanced content-based routing. |[`rules`](#Rules) | No* | +| `route` | The name of a VirtualServerRoute resource that defines this route. If the VirtualServerRoute belongs to a different namespace than the VirtualServer, you need to include the namespace. For example, `tea-namespace/tea`. | `string` | No* | + +\* -- a route must include exactly one of the following: `upstream`, `splits`, `rules` or `route`. + + +## VirtualServerRoute Specification + +The VirtualServerRoute resource defines a route for a VirtualServer. It can consist of one or multiple subroutes. The VirtualServerRoute is an alternative to [Mergeable Ingress types](../examples/mergeable-ingress-types/README.md). + +In the example below, the VirtualServer `cafe` from the namespace `cafe-ns` defines a route with the path `/coffee`, which is further defined in the VirtualServerRoute `coffee` from the namespace `coffee-ns`. + +VirtualServer: +```yaml +apiVersion: k8s.nginx.org/v1alpha1 +kind: VirtualServer +metadata: + name: cafe + namespace: cafe-ns +spec: + host: cafe.example.com + upstreams: + - name: tea + service: tea-svc + port: 80 + routes: + - path: /tea + upstream: tea + - path: /coffee + route: coffee-ns/coffee +``` + +VirtualServerRoute: +```yaml +apiVersion: k8s.nginx.org/v1alpha1 +kind: VirtualServerRoute +metadata: + name: coffee + namespace: coffee-ns +spec: + host: cafe.example.com + upstreams: + - name: latte + service: latte-svc + port: 80 + - name: espresso + service: espresso-svc + port: 80 + subroutes: + - path: /coffee/latte + upstream: latte + - path: /coffee/espresso + upstream: espresso +``` + +Note that each subroute must have a `path` that starts with the same prefix (here `/coffee`), which is defined in the route of the VirtualServer. Additionally, the `host` in the VirtualServerRoute must be the same as the `host` of the VirtualServer. + +| Field | Description | Type | Required | +| ----- | ----------- | ---- | -------- | +| `host` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as `my-app` or `hello.example.com`. Wildcard domains like `*.example.com` are not allowed. Must be the same as the `host` of the VirtualServer that references this resource. | `string` | Yes | +| `upstreams` | A list of upstreams. | [`[]upstream`](#Upstream) | No | +| `subroutes` | A list of subroutes. | [`[]subroute`](#VirtualServerRouteSubroute) | No | + +### VirtualServerRoute.Subroute + +The subroute defines rules for routing requests to one or multiple upstreams. For example: +```yaml +path: /coffee +upstream: coffee +``` + +| Field | Description | Type | Required | +| ----- | ----------- | ---- | -------- | +| `path` | The path of the subroute. NGINX will match it against the URI of a request. The path must start with the same path as the path of the route of the VirtualServer that references this resource. It must not include any whitespace characters, `{`, `}` or `;`. The path must be unique among the paths of all subroutes of the VirtualServerRoute. | `string` | Yes | +| `upstream` | The name of an upstream. The upstream with that name must be defined in the VirtualServerRoute. | `string` | No* | +| `splits` | The splits configuration for traffic splitting. Must include at least 2 splits. | [`[]splits`](#Split) | No* | +| `rules` | The rules configuration advanced content-based routing. |[`rules`](#Rules) | No* | + +\* -- a subroute must include exactly one of the following: `upstream`, `splits` or `rules`. + +## Common Parts of the VirtualServer and VirtualServerRoute + +### Upstream + +The upstream defines a destination for the routing configuration. For example: +```yaml +name: tea +service: tea-svc +port: 80 +``` + +| Field | Description | Type | Required | +| ----- | ----------- | ---- | -------- | +| `name` | The name of the upstream. Must be a valid DNS label as defined in RFC 1035. For example, `hello` and `upstream-123` are valid. The name must be unique among all upstreams of the resource. | `string` | Yes | +| `service` | The name of a [service](https://kubernetes.io/docs/concepts/services-networking/service/). The service must belong to the same namespace as the resource. If the service doesn't exist, NGINX will assume the service has zero endpoints and return a `502` response for requests for this upstream. | `string` | Yes | +| `port` | The port of the service. If the service doesn't define that port, NGINX will assume the service has zero endpoints and return a `502` response for requests for this upstream. The port must fall into the range `1..65553`. | `uint16` | Yes | + +### Split + +The split defines a weight for an upstream as part of the splits configuration. + +In the example below NGINX routes 80% of requests to the upstream `coffee-v1` and the remaining 20% to `coffee-v2`: +```yaml +splits: +- weight: 80 + upstream: coffee-v1 +- weight: 20 + upstream: coffee-v2 +``` + +| Field | Description | Type | Required | +| ----- | ----------- | ---- | -------- | +| `weight` | The weight of an upstream. Must fall into the range `1..99`. The sum of the weights of all splits must be equal to `100`. | `int` | Yes | +| `upstream` | The name of an upstream. Must be defined in the resource. | `string` | Yes | + +### Rules + +The rules defines a set of content-based routing rules in a route or subroute. + +In the example below, NGINX routes requests with the path `/coffee` to different upstreams based on the value of the cookie `user`: +* `user=john` -> `coffee-future` +* `user=bob` -> `coffee-deprecated` +* If the cookie is not set or not equal to either `john` or `bob`, NGINX routes to `coffee-stable` + +```yaml +path: /coffee +rules: + conditions: + - cookie: user + matches: + - values: + - john + upstream: coffee-future + - values: + - bob + upstream: coffee-deprecated + defaultUpstream: coffee-stable +``` + +In the next example, NGINX routes requests based on the value of the built-in [`$request_method` variable](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_method), which represents the HTTP method of a request: +* all POST requests -> `coffee-post` +* all non-POST requests -> `coffee` + +```yaml +path: /coffee +rules: + conditions: + - variable: $request_method + matches: + - values: + - POST + upstream: coffee-post + defaultUpstream: coffee +``` + +| Field | Description | Type | Required | +| ----- | ----------- | ---- | -------- | +| `conditions` | A list of conditions. Must include at least 1 condition. | [`[]condition`](#Condition) | Yes | +| `matches` | A list of matches. Must include at least 1 match. | [`[]match`](#Match) | Yes | +| `defaultUpstream` | The name of the default upstream. NGINX will route requests to the default upstream if it cannot find a successful match in matches. The upstream must be defined in the resource. | `string` | Yes | + +### Condition + +The condition defines a condition in rules. + +| Field | Description | Type | Required | +| ----- | ----------- | ---- | -------- | +| `header` | The name of a header. Must consist of alphanumeric characters or `-`. | `string` | No* | +| `cookie` | The name of a cookie. Must consist of alphanumeric characters or `_`. | `string` | No* | +| `argument` | The name of an argument. Must consist of alphanumeric characters or `_`. | `string` | No* | +| `variable` | The name of an NGINX variable. Must start with `$`. See the list of the supported variables below the table. | `string` | No* | + +\* -- a condition must include exactly one of the following: `header`, `cookie`, `argument` or `variable`. + +Supported NGINX variables: `$args`, `$http2`, `$https`, `$remote_addr`, `$remote_port`, `$query_string`, `$request`, `$request_body`, `$request_uri`, `$request_method`, `$scheme`. Find the documentation for each variable [here](https://nginx.org/en/docs/varindex.html). + +### Match + +The match defines a match that corresponds to conditions. + + +| Field | Description | Type | Required | +| ----- | ----------- | ---- | -------- | +| `values` | A list of matched values. Must include a value for each condition defined in the rules. How to define a value is shown below the table. | `[]string` | Yes | +| `upstream` | The name of an upstream. Must be defined in the resource. | `string` | Yes | + +The value supports two kinds of matching: +* *Case-insensitive string comparison*. For example: + * `john` -- case-insensitive matching that succeeds for strings, such as `john`, `John`, `JOHN`. + * `!john` -- negation of the case-incentive matching for john that succeeds for strings, such as `bob`, `anything`, `''` (empty string). +* *Matching with a regular expression*. Note that NGINX supports regular expressions compatible with those used by the Perl programming language (PCRE). For example: + * `~^yes` -- a case-sensitive regular expression that matches any string that starts with `yes`. For example: `yes`, `yes123`. + * `!~^yes` -- negation of the previous regular expression that succeeds for strings like `YES`, `Yes123`, `noyes`. (The negation mechanism is not part of the PCRE syntax). + * `~*no$` -- a case-insensitive regular expression that matches any string that ends with `no`. For example: `no`, `123no`, `123NO`. + +**Note**: a value must not include any unescaped double quotes (`"`) and must not end with an unescaped backslash (`\`). For example, the following are invalid values: `some"value`, `somevalue\`. + +## Using VirtualServer and VirtualServerRoute + +You can use the usual `kubectl` commands to work with VirtualServer and VirtualServerRoute resources, similar to Ingress resources. + +For example, the following command creates a VirtualServer resource defined in `cafe-virtual-server.yaml` with the name `cafe`: +``` +$ kubectl apply -f cafe-virtual-server.yaml +virtualserver.k8s.nginx.org "cafe" created +``` + +You can get the resource by running: +``` +$ kubectl get virtualserver cafe +NAME AGE +cafe 3m +``` + +In the kubectl get and similar commands, you can also use the short name `vs` instead of `virtualserver`. + +Working with VirtualServerRoute resources is analogous. In the kubectl commands, use `virtualserverroute` or the short name `vsr`. + +### Validation + +The Ingress Controller validates VirtualServer and VirtualServerRoute resources. If a resource is invalid, the Ingress Controller will reject it. + +You can check if the Ingress Controller successfully applied the configuration for a VirtualServer. For our example `cafe` VirtualServer, we can run: +``` +$ kubectl describe vs cafe +. . . +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal AddedOrUpdated 16s nginx-ingress-controller Configuration for default/cafe was added or updated +``` +Note how the events section includes a Normal event with the AddedOrUpdated reason that informs us that the configuration was successfully applied. + +If you create an invalid resource, the Ingress Controller will reject it and emit a Rejected event. For example, if you create a VirtualServer `cafe` with an empty `host` field, you will get: +``` +$ kubectl describe vs cafe +. . . +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Warning Rejected 2s nginx-ingress-controller VirtualServer default/cafe is invalid and was rejected: spec.host: Required value +``` +Note how the events section includes a Warning event with the Rejected reason. + +The Ingress Controller validates VirtualServerRoute resources in a similar way. + +**Note**: If you make an existing resource invalid, the Ingress Controller will reject it and remove the corresponding configuration from NGINX. + +## Customization via ConfigMap + +You can customize the NGINX configuration for VirtualServer and VirtualServerRoutes resources using the [ConfigMap](configmap-and-annotations.md). Most of the ConfigMap keys are supported, with the following exceptions: +* `proxy-hide-headers` +* `proxy-pass-headers` +* `hsts` +* `hsts-max-age` +* `hsts-include-subdomains` +* `hsts-behind-proxy` diff --git a/examples-of-custom-resources/advanced-routing/README.md b/examples-of-custom-resources/advanced-routing/README.md new file mode 100644 index 0000000000..5466d7dbd5 --- /dev/null +++ b/examples-of-custom-resources/advanced-routing/README.md @@ -0,0 +1,79 @@ +# Advanced Routing + +In this example we use the [VirtualServer](../../docs/virtualserver-and-virtualserverroute.md) resource to configure advanced routing for the cafe application from the [Basic Configuration](../basic-configuration/) example, for which we have introduced the following changes: +* Instead of one version of the tea service, we have two: `tea-post-svc` and `tea-svc`. We send POST requests for tea to `tea-post-svc` and non-POST requests, such as GET requests, to `tea-svc`. +* Instead of one version of the coffee service, we have two: `coffee-v1-svc` and `coffee-v2-svc`. We send requests that include the cookie `version` set to `v2` to `coffee-v2-svc` and all other requests to `coffee-v1-svc`. +* To simplify the example, we have removed TLS termination. + +## Prerequisites + +1. Follow the [installation](../../docs/installation.md) instructions to deploy the Ingress Controller with custom resources enabled. +1. Save the public IP address of the Ingress Controller into a shell variable: + ``` + $ IC_IP=XXX.YYY.ZZZ.III + ``` +1. Save the HTTP port of the Ingress Controller into a shell variable: + ``` + $ IC_HTTP_PORT= + ``` + +## Step 1 - Deploy the Cafe Application + +Create the coffee and the tea deployments and services: +``` +$ kubectl create -f cafe.yaml +``` + +## Step 2 - Configure Load Balancing + +Create the VirtualServer resource: +``` +$ kubectl create -f cafe-virtual-server.yaml +``` + +## Step 3 - Test the Configuration + +1. Check that the configuration has been successfully applied by inspecting the events of the VirtualServer: + ``` + $ kubectl describe virtualserver cafe + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal AddedOrUpdated 2s nginx-ingress-controller Configuration for default/cafe was added or updated + ``` +1. Access the tea service using curl. We'll use curl's `--resolve` option to set the IP address and HTTP port of the Ingress Controller to the domain name of the cafe application: + + Send a POST request and confirm that the response comes from `tea-post-svc`: + ``` + $ curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/tea -X POST + Server address: 10.16.1.188:80 + Server name: tea-post-b5dd479b4-6ssmh + . . . + ``` + + Send a GET request and confirm that the response comes from `tea-svc`: + ``` + $ curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/tea + Server address: 10.16.1.189:80 + Server name: tea-7d57856c44-2hsvr + . . . + ``` + +1. Access the coffee service: + + Send a request with the cookie `version=v2` and confirm that the response comes from `coffee-v2-svc`: + ``` + $ curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/coffee --cookie "version=v2" + Server address: 10.16.1.187:80 + Server name: coffee-v2-7fd446968b-vkthp + . . . + ``` + + Send a request without the cookie and confirm that the response comes from `coffee-v1-svc`: + ``` + $ curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/coffee + Server address: 10.16.0.153:80 + Server name: coffee-v1-78754bdcfb-bs9nh + . . . + ``` \ No newline at end of file diff --git a/examples-of-custom-resources/advanced-routing/cafe-virtual-server.yaml b/examples-of-custom-resources/advanced-routing/cafe-virtual-server.yaml new file mode 100644 index 0000000000..da7689ce93 --- /dev/null +++ b/examples-of-custom-resources/advanced-routing/cafe-virtual-server.yaml @@ -0,0 +1,38 @@ +apiVersion: k8s.nginx.org/v1alpha1 +kind: VirtualServer +metadata: + name: cafe +spec: + host: cafe.example.com + upstreams: + - name: tea-post + service: tea-post-svc + port: 80 + - name: tea + service: tea-svc + port: 80 + - name: coffee-v1 + service: coffee-v1-svc + port: 80 + - name: coffee-v2 + service: coffee-v2-svc + port: 80 + routes: + - path: /tea + rules: + conditions: + - variable: $request_method + matches: + - values: + - POST + upstream: tea-post + defaultUpstream: tea + - path: /coffee + rules: + conditions: + - cookie: version + matches: + - values: + - v2 + upstream: coffee-v2 + defaultUpstream: coffee-v1 \ No newline at end of file diff --git a/examples-of-custom-resources/advanced-routing/cafe.yaml b/examples-of-custom-resources/advanced-routing/cafe.yaml new file mode 100644 index 0000000000..e844153e6b --- /dev/null +++ b/examples-of-custom-resources/advanced-routing/cafe.yaml @@ -0,0 +1,131 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coffee-v1 +spec: + replicas: 1 + selector: + matchLabels: + app: coffee-v1 + template: + metadata: + labels: + app: coffee-v1 + spec: + containers: + - name: coffee-v1 + image: nginxdemos/hello:plain-text + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: coffee-v1-svc +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: coffee-v1 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coffee-v2 +spec: + replicas: 1 + selector: + matchLabels: + app: coffee-v2 + template: + metadata: + labels: + app: coffee-v2 + spec: + containers: + - name: coffee-v2 + image: nginxdemos/hello:plain-text + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: coffee-v2-svc +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: coffee-v2 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tea-post +spec: + replicas: 1 + selector: + matchLabels: + app: tea-post + template: + metadata: + labels: + app: tea-post + spec: + containers: + - name: tea-post + image: nginxdemos/hello:plain-text + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: tea-post-svc +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: tea-post +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tea +spec: + replicas: 1 + selector: + matchLabels: + app: tea + template: + metadata: + labels: + app: tea + spec: + containers: + - name: tea + image: nginxdemos/hello:plain-text + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: tea-svc +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: tea \ No newline at end of file diff --git a/examples-of-custom-resources/basic-configuration/README.md b/examples-of-custom-resources/basic-configuration/README.md new file mode 100644 index 0000000000..670e593cbf --- /dev/null +++ b/examples-of-custom-resources/basic-configuration/README.md @@ -0,0 +1,63 @@ +# Basic Configuration + +In this example we configure load balancing with TLS termination for a simple web application using the [VirtualServer](../../docs/virtualserver-and-virtualserverroute.md) resource. The application, called cafe, lets you get either tea via the tea service or coffee via the coffee service. You indicate your drink preference with the URI of your HTTP request: URIs ending with `/tea` get you tea and URIs ending with `/coffee` get you coffee. + +The example is similar to the [complete example](../../examples/complete-example/README.md). However, instead of the Ingress resource, we use the VirtualServer. + +## Prerequisites + +1. Follow the [installation](../../docs/installation.md) instructions to deploy the Ingress Controller with custom resources enabled. +1. Save the public IP address of the Ingress Controller into a shell variable: + ``` + $ IC_IP=XXX.YYY.ZZZ.III + ``` +1. Save the HTTPS port of the Ingress Controller into a shell variable: + ``` + $ IC_HTTPS_PORT= + ``` + +## Step 1 - Deploy the Cafe Application + +Create the coffee and the tea deployments and services: +``` +$ kubectl create -f cafe.yaml +``` + +## Step 2 - Configure Load Balancing and TLS Termination + +1. Create the secret with the TLS certificate and key: + ``` + $ kubectl create -f cafe-secret.yaml + ``` + +2. Create the VirtualServer resource: + ``` + $ kubectl create -f cafe-virtual-server.yaml + ``` + +## Step 3 - Test the Configuration + +1. Check that the configuration has been successfully applied by inspecting the events of the VirtualServer: + ``` + $ kubectl describe virtualserver cafe + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal AddedOrUpdated 7s nginx-ingress-controller Configuration for default/cafe was added or updated + ``` +1. Access the application using curl. We'll use curl's `--insecure` option to turn off certificate verification of our self-signed certificate and `--resolve` option to set the IP address and HTTPS port of the Ingress Controller to the domain name of the cafe application: + + To get coffee: + ``` + $ curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/coffee --insecure + Server address: 10.16.1.182:80 + Server name: coffee-7dbb5795f6-tnbtq + ... + ``` + If your prefer tea: + ``` + $ curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/tea --insecure + Server address: 10.16.0.149:80 + Server name: tea-7d57856c44-zlftd + ... \ No newline at end of file diff --git a/examples-of-custom-resources/basic-configuration/cafe-secret.yaml b/examples-of-custom-resources/basic-configuration/cafe-secret.yaml new file mode 100644 index 0000000000..f9993a66e8 --- /dev/null +++ b/examples-of-custom-resources/basic-configuration/cafe-secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: cafe-secret +type: Opaque +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhZQ0NRREFPRjl0THNhWFdqQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MApaREViTUJrR0ExVUVBd3dTWTJGbVpTNWxlR0Z0Y0d4bExtTnZiU0FnTUI0WERURTRNRGt4TWpFMk1UVXpOVm9YCkRUSXpNRGt4TVRFMk1UVXpOVm93V0RFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVNFd0h3WUQKVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReEdUQVhCZ05WQkFNTUVHTmhabVV1WlhoaApiWEJzWlM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcDZLbjdzeTgxCnAwanVKL2N5ayt2Q0FtbHNmanRGTTJtdVpOSzBLdGVjcUcyZmpXUWI1NXhRMVlGQTJYT1N3SEFZdlNkd0kyaloKcnVXOHFYWENMMnJiNENaQ0Z4d3BWRUNyY3hkam0zdGVWaVJYVnNZSW1tSkhQUFN5UWdwaW9iczl4N0RsTGM2SQpCQTBaalVPeWwwUHFHOVNKZXhNVjczV0lJYTVyRFZTRjJyNGtTa2JBajREY2o3TFhlRmxWWEgySTVYd1hDcHRDCm42N0pDZzQyZitrOHdnemNSVnA4WFprWldaVmp3cTlSVUtEWG1GQjJZeU4xWEVXZFowZXdSdUtZVUpsc202OTIKc2tPcktRajB2a29QbjQxRUUvK1RhVkVwcUxUUm9VWTNyemc3RGtkemZkQml6Rk8yZHNQTkZ4MkNXMGpYa05MdgpLbzI1Q1pyT2hYQUhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLSEZDY3lPalp2b0hzd1VCTWRMClJkSEliMzgzcFdGeW5acS9MdVVvdnNWQTU4QjBDZzdCRWZ5NXZXVlZycTVSSWt2NGxaODFOMjl4MjFkMUpINnIKalNuUXgrRFhDTy9USkVWNWxTQ1VwSUd6RVVZYVVQZ1J5anNNL05VZENKOHVIVmhaSitTNkZBK0NuT0Q5cm4yaQpaQmVQQ0k1ckh3RVh3bm5sOHl3aWozdnZRNXpISXV5QmdsV3IvUXl1aTlmalBwd1dVdlVtNG52NVNNRzl6Q1Y3ClBwdXd2dWF0cWpPMTIwOEJqZkUvY1pISWc4SHc5bXZXOXg5QytJUU1JTURFN2IvZzZPY0s3TEdUTHdsRnh2QTgKN1dqRWVxdW5heUlwaE1oS1JYVmYxTjM0OWVOOThFejM4Zk9USFRQYmRKakZBL1BjQytHeW1lK2lHdDVPUWRGaAp5UkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcWVpcCs3TXZOYWRJN2lmM01wUHJ3Z0pwYkg0N1JUTnBybVRTdENyWG5LaHRuNDFrCkcrZWNVTldCUU5semtzQndHTDBuY0NObzJhN2x2S2wxd2k5cTIrQW1RaGNjS1ZSQXEzTVhZNXQ3WGxZa1YxYkcKQ0pwaVJ6ejBza0lLWXFHN1BjZXc1UzNPaUFRTkdZMURzcGRENmh2VWlYc1RGZTkxaUNHdWF3MVVoZHErSkVwRwp3SStBM0kreTEzaFpWVng5aU9WOEZ3cWJRcCt1eVFvT05uL3BQTUlNM0VWYWZGMlpHVm1WWThLdlVWQ2cxNWhRCmRtTWpkVnhGbldkSHNFYmltRkNaYkp1dmRySkRxeWtJOUw1S0Q1K05SQlAvazJsUkthaTAwYUZHTjY4NE93NUgKYzMzUVlzeFR0bmJEelJjZGdsdEkxNURTN3lxTnVRbWF6b1Z3QndJREFRQUJBb0lCQVFDUFNkU1luUXRTUHlxbApGZlZGcFRPc29PWVJoZjhzSStpYkZ4SU91UmF1V2VoaEp4ZG01Uk9ScEF6bUNMeUw1VmhqdEptZTIyM2dMcncyCk45OUVqVUtiL1ZPbVp1RHNCYzZvQ0Y2UU5SNThkejhjbk9SVGV3Y290c0pSMXBuMWhobG5SNUhxSkpCSmFzazEKWkVuVVFmY1hackw5NGxvOUpIM0UrVXFqbzFGRnM4eHhFOHdvUEJxalpzVjdwUlVaZ0MzTGh4bndMU0V4eUZvNApjeGI5U09HNU9tQUpvelN0Rm9RMkdKT2VzOHJKNXFmZHZ5dGdnOXhiTGFRTC94MGtwUTYyQm9GTUJEZHFPZVBXCktmUDV6WjYvMDcvdnBqNDh5QTFRMzJQem9idWJzQkxkM0tjbjMyamZtMUU3cHJ0V2wrSmVPRmlPem5CUUZKYk4KNHFQVlJ6NWhBb0dCQU50V3l4aE5DU0x1NFArWGdLeWNrbGpKNkY1NjY4Zk5qNUN6Z0ZScUowOXpuMFRsc05ybwpGVExaY3hEcW5SM0hQWU00MkpFUmgySi9xREZaeW5SUW8zY2czb2VpdlVkQlZHWTgrRkkxVzBxZHViL0w5K3l1CmVkT1pUUTVYbUdHcDZyNmpleHltY0ppbS9Pc0IzWm5ZT3BPcmxEN1NQbUJ2ek5MazRNRjZneGJYQW9HQkFNWk8KMHA2SGJCbWNQMHRqRlhmY0tFNzdJbUxtMHNBRzR1SG9VeDBlUGovMnFyblRuT0JCTkU0TXZnRHVUSnp5K2NhVQprOFJxbWRIQ2JIelRlNmZ6WXEvOWl0OHNaNzdLVk4xcWtiSWN1YytSVHhBOW5OaDFUanNSbmU3NFowajFGQ0xrCmhIY3FIMHJpN1BZU0tIVEU4RnZGQ3haWWRidUI4NENtWmlodnhicFJBb0dBSWJqcWFNWVBUWXVrbENkYTVTNzkKWVNGSjFKelplMUtqYS8vdER3MXpGY2dWQ0thMzFqQXdjaXowZi9sU1JxM0hTMUdHR21lemhQVlRpcUxmZVpxYwpSMGlLYmhnYk9jVlZrSkozSzB5QXlLd1BUdW14S0haNnpJbVpTMGMwYW0rUlk5WUdxNVQ3WXJ6cHpjZnZwaU9VCmZmZTNSeUZUN2NmQ21mb09oREN0enVrQ2dZQjMwb0xDMVJMRk9ycW40M3ZDUzUxemM1em9ZNDR1QnpzcHd3WU4KVHd2UC9FeFdNZjNWSnJEakJDSCtULzZzeXNlUGJKRUltbHpNK0l3eXRGcEFOZmlJWEV0LzQ4WGY2ME54OGdXTQp1SHl4Wlp4L05LdER3MFY4dlgxUE9ucTJBNWVpS2ErOGpSQVJZS0pMWU5kZkR1d29seHZHNmJaaGtQaS80RXRUCjNZMThzUUtCZ0h0S2JrKzdsTkpWZXN3WEU1Y1VHNkVEVXNEZS8yVWE3ZlhwN0ZjanFCRW9hcDFMU3crNlRYcDAKWmdybUtFOEFSek00NytFSkhVdmlpcS9udXBFMTVnMGtKVzNzeWhwVTl6WkxPN2x0QjBLSWtPOVpSY21Vam84UQpjcExsSE1BcWJMSjhXWUdKQ2toaVd4eWFsNmhZVHlXWTRjVmtDMHh0VGwvaFVFOUllTktvCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== diff --git a/examples-of-custom-resources/basic-configuration/cafe-virtual-server.yaml b/examples-of-custom-resources/basic-configuration/cafe-virtual-server.yaml new file mode 100644 index 0000000000..192d3123a5 --- /dev/null +++ b/examples-of-custom-resources/basic-configuration/cafe-virtual-server.yaml @@ -0,0 +1,20 @@ +apiVersion: k8s.nginx.org/v1alpha1 +kind: VirtualServer +metadata: + name: cafe +spec: + host: cafe.example.com + tls: + secret: cafe-secret + upstreams: + - name: tea + service: tea-svc + port: 80 + - name: coffee + service: coffee-svc + port: 80 + routes: + - path: /tea + upstream: tea + - path: /coffee + upstream: coffee diff --git a/examples-of-custom-resources/basic-configuration/cafe.yaml b/examples-of-custom-resources/basic-configuration/cafe.yaml new file mode 100644 index 0000000000..f37772a5e3 --- /dev/null +++ b/examples-of-custom-resources/basic-configuration/cafe.yaml @@ -0,0 +1,65 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coffee +spec: + replicas: 2 + selector: + matchLabels: + app: coffee + template: + metadata: + labels: + app: coffee + spec: + containers: + - name: coffee + image: nginxdemos/hello:plain-text + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: coffee-svc +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: coffee +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tea +spec: + replicas: 1 + selector: + matchLabels: + app: tea + template: + metadata: + labels: + app: tea + spec: + containers: + - name: tea + image: nginxdemos/hello:plain-text + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: tea-svc +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: tea \ No newline at end of file diff --git a/examples-of-custom-resources/cross-namespace-configuration/README.md b/examples-of-custom-resources/cross-namespace-configuration/README.md new file mode 100644 index 0000000000..614fc3606c --- /dev/null +++ b/examples-of-custom-resources/cross-namespace-configuration/README.md @@ -0,0 +1,98 @@ +# Cross-Namespace Configuration + +In this example we use the [VirtualServer and VirtualServerRoute](../../docs/virtualserver-and-virtualserverroute.md) resources to configure load balancing for the modified cafe application from the [Basic Configuration](../basic-configuration/) example. We have put the load balancing configuration as well as the deployments and services into multiple namespaces. Instead of one namespace, we now use three: `tea`, `coffee`, and `cafe`. +* In the tea namespace, we create the tea deployment, service, and the corresponding load-balancing configuration. +* In the coffee namespace, we create the coffee deployment, service, and the corresponding load-balancing configuration. +* In the cafe namespace, we create the cafe secret with the TLS certificate and key and the load-balancing configuration for the cafe application. That configuration references the coffee and tea configurations. + +## Prerequisites + +1. Follow the [installation](../../docs/installation.md) instructions to deploy the Ingress Controller with custom resources enabled. +1. Save the public IP address of the Ingress Controller into a shell variable: + ``` + $ IC_IP=XXX.YYY.ZZZ.III + ``` +1. Save the HTTPS port of the Ingress Controller into a shell variable: + ``` + $ IC_HTTPS_PORT= + ``` + +## Step 1 - Create Namespaces + +Create the required tea, coffee, and cafe namespaces: +``` +$ kubectl create -f namespaces.yaml +``` + +## Step 2 - Deploy the Cafe Application + +1. Create the tea deployment and service in the tea namespace: + ``` + $ kubectl create -f tea.yaml + ``` +1. Create the coffee deployment and service in the coffee namespace: + ``` + $ kubectl create -f coffee.yaml + ``` + +## Step 3 - Configure Load Balancing and TLS Termination + +1. Create the VirtualServerRoute resource for tea in the tea namespace: + ``` + $ kubectl create -f tea-virtual-server-route.yaml + ``` +1. Create the VirtualServerRoute resource for coffee in the coffee namespace: + ``` + $ kubectl create -f coffee-virtual-server-route.yaml + ``` +1. Create the secret with the TLS certificate and key in the cafe namespace: + ``` + $ kubectl create -f cafe-secret.yaml + ``` +1. Create the VirtualServer resource for the cafe app in the cafe namespace: + ``` + $ kubectl create -f cafe-virtual-server.yaml + ``` + +## Step 4 - Test the Configuration + +1. Check that the configuration has been successfully applied by inspecting the events of the VirtualServerRoutes and VirtualServer: + ``` + $ kubectl describe virtualserverroute tea -n tea + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Warning NoVirtualServersFound 2m nginx-ingress-controller No VirtualServer references VirtualServerRoute tea/tea + Normal AddedOrUpdated 1m nginx-ingress-controller Configuration for tea/tea was added or updated + + $ kubectl describe virtualserverroute coffee -n coffee + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Warning NoVirtualServersFound 2m nginx-ingress-controller No VirtualServer references VirtualServerRoute coffee/coffee + Normal AddedOrUpdated 1m nginx-ingress-controller Configuration for coffee/coffee was added or updated + + $ kubectl describe virtualserver cafe -n cafe + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal AddedOrUpdated 1m nginx-ingress-controller Configuration for cafe/cafe was added or updated + ``` +1. Access the application using curl. We'll use curl's `--insecure` option to turn off certificate verification of our self-signed certificate and `--resolve` option to set the IP address and HTTPS port of the Ingress Controller to the domain name of the cafe application: + + To get coffee: + ``` + $ curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/coffee --insecure + Server address: 10.16.1.193:80 + Server name: coffee-7dbb5795f6-mltpf + ... + ``` + If your prefer tea: + ``` + $ curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/tea --insecure + Server address: 10.16.0.157:80 + Server name: tea-7d57856c44-674b8 + ... \ No newline at end of file diff --git a/examples-of-custom-resources/cross-namespace-configuration/cafe-secret.yaml b/examples-of-custom-resources/cross-namespace-configuration/cafe-secret.yaml new file mode 100644 index 0000000000..ad4e33b15b --- /dev/null +++ b/examples-of-custom-resources/cross-namespace-configuration/cafe-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: cafe-secret + namespace: cafe +type: Opaque +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhZQ0NRREFPRjl0THNhWFdqQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MApaREViTUJrR0ExVUVBd3dTWTJGbVpTNWxlR0Z0Y0d4bExtTnZiU0FnTUI0WERURTRNRGt4TWpFMk1UVXpOVm9YCkRUSXpNRGt4TVRFMk1UVXpOVm93V0RFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVNFd0h3WUQKVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReEdUQVhCZ05WQkFNTUVHTmhabVV1WlhoaApiWEJzWlM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcDZLbjdzeTgxCnAwanVKL2N5ayt2Q0FtbHNmanRGTTJtdVpOSzBLdGVjcUcyZmpXUWI1NXhRMVlGQTJYT1N3SEFZdlNkd0kyaloKcnVXOHFYWENMMnJiNENaQ0Z4d3BWRUNyY3hkam0zdGVWaVJYVnNZSW1tSkhQUFN5UWdwaW9iczl4N0RsTGM2SQpCQTBaalVPeWwwUHFHOVNKZXhNVjczV0lJYTVyRFZTRjJyNGtTa2JBajREY2o3TFhlRmxWWEgySTVYd1hDcHRDCm42N0pDZzQyZitrOHdnemNSVnA4WFprWldaVmp3cTlSVUtEWG1GQjJZeU4xWEVXZFowZXdSdUtZVUpsc202OTIKc2tPcktRajB2a29QbjQxRUUvK1RhVkVwcUxUUm9VWTNyemc3RGtkemZkQml6Rk8yZHNQTkZ4MkNXMGpYa05MdgpLbzI1Q1pyT2hYQUhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLSEZDY3lPalp2b0hzd1VCTWRMClJkSEliMzgzcFdGeW5acS9MdVVvdnNWQTU4QjBDZzdCRWZ5NXZXVlZycTVSSWt2NGxaODFOMjl4MjFkMUpINnIKalNuUXgrRFhDTy9USkVWNWxTQ1VwSUd6RVVZYVVQZ1J5anNNL05VZENKOHVIVmhaSitTNkZBK0NuT0Q5cm4yaQpaQmVQQ0k1ckh3RVh3bm5sOHl3aWozdnZRNXpISXV5QmdsV3IvUXl1aTlmalBwd1dVdlVtNG52NVNNRzl6Q1Y3ClBwdXd2dWF0cWpPMTIwOEJqZkUvY1pISWc4SHc5bXZXOXg5QytJUU1JTURFN2IvZzZPY0s3TEdUTHdsRnh2QTgKN1dqRWVxdW5heUlwaE1oS1JYVmYxTjM0OWVOOThFejM4Zk9USFRQYmRKakZBL1BjQytHeW1lK2lHdDVPUWRGaAp5UkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcWVpcCs3TXZOYWRJN2lmM01wUHJ3Z0pwYkg0N1JUTnBybVRTdENyWG5LaHRuNDFrCkcrZWNVTldCUU5semtzQndHTDBuY0NObzJhN2x2S2wxd2k5cTIrQW1RaGNjS1ZSQXEzTVhZNXQ3WGxZa1YxYkcKQ0pwaVJ6ejBza0lLWXFHN1BjZXc1UzNPaUFRTkdZMURzcGRENmh2VWlYc1RGZTkxaUNHdWF3MVVoZHErSkVwRwp3SStBM0kreTEzaFpWVng5aU9WOEZ3cWJRcCt1eVFvT05uL3BQTUlNM0VWYWZGMlpHVm1WWThLdlVWQ2cxNWhRCmRtTWpkVnhGbldkSHNFYmltRkNaYkp1dmRySkRxeWtJOUw1S0Q1K05SQlAvazJsUkthaTAwYUZHTjY4NE93NUgKYzMzUVlzeFR0bmJEelJjZGdsdEkxNURTN3lxTnVRbWF6b1Z3QndJREFRQUJBb0lCQVFDUFNkU1luUXRTUHlxbApGZlZGcFRPc29PWVJoZjhzSStpYkZ4SU91UmF1V2VoaEp4ZG01Uk9ScEF6bUNMeUw1VmhqdEptZTIyM2dMcncyCk45OUVqVUtiL1ZPbVp1RHNCYzZvQ0Y2UU5SNThkejhjbk9SVGV3Y290c0pSMXBuMWhobG5SNUhxSkpCSmFzazEKWkVuVVFmY1hackw5NGxvOUpIM0UrVXFqbzFGRnM4eHhFOHdvUEJxalpzVjdwUlVaZ0MzTGh4bndMU0V4eUZvNApjeGI5U09HNU9tQUpvelN0Rm9RMkdKT2VzOHJKNXFmZHZ5dGdnOXhiTGFRTC94MGtwUTYyQm9GTUJEZHFPZVBXCktmUDV6WjYvMDcvdnBqNDh5QTFRMzJQem9idWJzQkxkM0tjbjMyamZtMUU3cHJ0V2wrSmVPRmlPem5CUUZKYk4KNHFQVlJ6NWhBb0dCQU50V3l4aE5DU0x1NFArWGdLeWNrbGpKNkY1NjY4Zk5qNUN6Z0ZScUowOXpuMFRsc05ybwpGVExaY3hEcW5SM0hQWU00MkpFUmgySi9xREZaeW5SUW8zY2czb2VpdlVkQlZHWTgrRkkxVzBxZHViL0w5K3l1CmVkT1pUUTVYbUdHcDZyNmpleHltY0ppbS9Pc0IzWm5ZT3BPcmxEN1NQbUJ2ek5MazRNRjZneGJYQW9HQkFNWk8KMHA2SGJCbWNQMHRqRlhmY0tFNzdJbUxtMHNBRzR1SG9VeDBlUGovMnFyblRuT0JCTkU0TXZnRHVUSnp5K2NhVQprOFJxbWRIQ2JIelRlNmZ6WXEvOWl0OHNaNzdLVk4xcWtiSWN1YytSVHhBOW5OaDFUanNSbmU3NFowajFGQ0xrCmhIY3FIMHJpN1BZU0tIVEU4RnZGQ3haWWRidUI4NENtWmlodnhicFJBb0dBSWJqcWFNWVBUWXVrbENkYTVTNzkKWVNGSjFKelplMUtqYS8vdER3MXpGY2dWQ0thMzFqQXdjaXowZi9sU1JxM0hTMUdHR21lemhQVlRpcUxmZVpxYwpSMGlLYmhnYk9jVlZrSkozSzB5QXlLd1BUdW14S0haNnpJbVpTMGMwYW0rUlk5WUdxNVQ3WXJ6cHpjZnZwaU9VCmZmZTNSeUZUN2NmQ21mb09oREN0enVrQ2dZQjMwb0xDMVJMRk9ycW40M3ZDUzUxemM1em9ZNDR1QnpzcHd3WU4KVHd2UC9FeFdNZjNWSnJEakJDSCtULzZzeXNlUGJKRUltbHpNK0l3eXRGcEFOZmlJWEV0LzQ4WGY2ME54OGdXTQp1SHl4Wlp4L05LdER3MFY4dlgxUE9ucTJBNWVpS2ErOGpSQVJZS0pMWU5kZkR1d29seHZHNmJaaGtQaS80RXRUCjNZMThzUUtCZ0h0S2JrKzdsTkpWZXN3WEU1Y1VHNkVEVXNEZS8yVWE3ZlhwN0ZjanFCRW9hcDFMU3crNlRYcDAKWmdybUtFOEFSek00NytFSkhVdmlpcS9udXBFMTVnMGtKVzNzeWhwVTl6WkxPN2x0QjBLSWtPOVpSY21Vam84UQpjcExsSE1BcWJMSjhXWUdKQ2toaVd4eWFsNmhZVHlXWTRjVmtDMHh0VGwvaFVFOUllTktvCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== diff --git a/examples-of-custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml b/examples-of-custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml new file mode 100644 index 0000000000..4760df0318 --- /dev/null +++ b/examples-of-custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml @@ -0,0 +1,14 @@ +apiVersion: k8s.nginx.org/v1alpha1 +kind: VirtualServer +metadata: + name: cafe + namespace: cafe +spec: + host: cafe.example.com + tls: + secret: cafe-secret + routes: + - path: /tea + route: tea/tea + - path: /coffee + route: coffee/coffee diff --git a/examples-of-custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml b/examples-of-custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml new file mode 100644 index 0000000000..88a71d7ea3 --- /dev/null +++ b/examples-of-custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml @@ -0,0 +1,14 @@ +apiVersion: k8s.nginx.org/v1alpha1 +kind: VirtualServerRoute +metadata: + name: coffee + namespace: coffee +spec: + host: cafe.example.com + upstreams: + - name: coffee + service: coffee-svc + port: 80 + subroutes: + - path: /coffee + upstream: coffee \ No newline at end of file diff --git a/examples-of-custom-resources/cross-namespace-configuration/coffee.yaml b/examples-of-custom-resources/cross-namespace-configuration/coffee.yaml new file mode 100644 index 0000000000..135a61558a --- /dev/null +++ b/examples-of-custom-resources/cross-namespace-configuration/coffee.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coffee + namespace: coffee +spec: + replicas: 1 + selector: + matchLabels: + app: coffee + template: + metadata: + labels: + app: coffee + spec: + containers: + - name: coffee + image: nginxdemos/hello:plain-text + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: coffee-svc + namespace: coffee +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: coffee \ No newline at end of file diff --git a/examples-of-custom-resources/cross-namespace-configuration/namespaces.yaml b/examples-of-custom-resources/cross-namespace-configuration/namespaces.yaml new file mode 100644 index 0000000000..8e7275f71d --- /dev/null +++ b/examples-of-custom-resources/cross-namespace-configuration/namespaces.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: cafe +--- +apiVersion: v1 +kind: Namespace +metadata: + name: tea +--- +apiVersion: v1 +kind: Namespace +metadata: + name: coffee \ No newline at end of file diff --git a/examples-of-custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml b/examples-of-custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml new file mode 100644 index 0000000000..2f63026eeb --- /dev/null +++ b/examples-of-custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml @@ -0,0 +1,14 @@ +apiVersion: k8s.nginx.org/v1alpha1 +kind: VirtualServerRoute +metadata: + name: tea + namespace: tea +spec: + host: cafe.example.com + upstreams: + - name: tea + service: tea-svc + port: 80 + subroutes: + - path: /tea + upstream: tea \ No newline at end of file diff --git a/examples-of-custom-resources/cross-namespace-configuration/tea.yaml b/examples-of-custom-resources/cross-namespace-configuration/tea.yaml new file mode 100644 index 0000000000..3f5643a639 --- /dev/null +++ b/examples-of-custom-resources/cross-namespace-configuration/tea.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tea + namespace: tea +spec: + replicas: 1 + selector: + matchLabels: + app: tea + template: + metadata: + labels: + app: tea + spec: + containers: + - name: tea + image: nginxdemos/hello:plain-text + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: tea-svc + namespace: tea +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: tea \ No newline at end of file diff --git a/examples-of-custom-resources/traffic-splitting/README.md b/examples-of-custom-resources/traffic-splitting/README.md new file mode 100644 index 0000000000..51ddcc018f --- /dev/null +++ b/examples-of-custom-resources/traffic-splitting/README.md @@ -0,0 +1,60 @@ +# Traffic Splitting + +In this example we use the [VirtualServer](../../docs/virtualserver-and-virtualserverroute.md) resource to configure traffic splitting for the cafe application from the [Basic Configuration](../basic-configuration/) example, for which we have introduced the following changes: +* Instead of one version of the coffee service, we have two: `coffee-v1-svc` and `coffee-v2-svc`. We send 90% of the coffee traffic to `coffee-v1-svc` and the remaining 10% to `coffee-v2-svc`. +* To simplify the example, we have removed TLS termination and the tea service. + +## Prerequisites + +1. Follow the [installation](../../docs/installation.md) instructions to deploy the Ingress Controller with custom resources enabled. +1. Save the public IP address of the Ingress Controller into a shell variable: + ``` + $ IC_IP=XXX.YYY.ZZZ.III + ``` +1. Save the HTTP port of the Ingress Controller into a shell variable: + ``` + $ IC_HTTP_PORT= + ``` + +## Step 1 - Deploy the Cafe Application + +Create the coffee deployments and services: +``` +$ kubectl create -f cafe.yaml +``` + +## Step 2 - Configure Load Balancing + +Create the VirtualServer resource: +``` +$ kubectl create -f cafe-virtual-server.yaml +``` + +## Step 3 - Test the Configuration + +1. Check that the configuration has been successfully applied by inspecting the events of the VirtualServer: + ``` + $ kubectl describe virtualserver cafe + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal AddedOrUpdated 5s nginx-ingress-controller Configuration for default/cafe was added or updated + ``` +1. Access the application using curl. We'll use curl's `--resolve` option to set the IP address and HTTP port of the Ingress Controller to the domain name of the cafe application. Try to get coffee multiple times to see how NGINX sends requests to different versions of the coffee service: + ``` + $ curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/coffee + ``` + + 90% of responses will come from `coffee-v1-svc`: + ``` + Server address: 10.16.0.151:80 + Server name: coffee-v1-78754bdcfb-7xp27 + ... + ``` + + 10 % of responses will come from `coffee-v2-svc`: + ``` + Server address: 10.16.0.152:80 + Server name: coffee-v2-7fd446968b-lwhgcd + ... \ No newline at end of file diff --git a/examples-of-custom-resources/traffic-splitting/cafe-virtual-server.yaml b/examples-of-custom-resources/traffic-splitting/cafe-virtual-server.yaml new file mode 100644 index 0000000000..d9719c425f --- /dev/null +++ b/examples-of-custom-resources/traffic-splitting/cafe-virtual-server.yaml @@ -0,0 +1,20 @@ +apiVersion: k8s.nginx.org/v1alpha1 +kind: VirtualServer +metadata: + name: cafe +spec: + host: cafe.example.com + upstreams: + - name: coffee-v1 + service: coffee-v1-svc + port: 80 + - name: coffee-v2 + service: coffee-v2-svc + port: 80 + routes: + - path: /coffee + splits: + - weight: 90 + upstream: coffee-v1 + - weight: 10 + upstream: coffee-v2 diff --git a/examples-of-custom-resources/traffic-splitting/cafe.yaml b/examples-of-custom-resources/traffic-splitting/cafe.yaml new file mode 100644 index 0000000000..55933667d2 --- /dev/null +++ b/examples-of-custom-resources/traffic-splitting/cafe.yaml @@ -0,0 +1,65 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coffee-v1 +spec: + replicas: 2 + selector: + matchLabels: + app: coffee-v1 + template: + metadata: + labels: + app: coffee-v1 + spec: + containers: + - name: coffee-v1 + image: nginxdemos/hello:plain-text + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: coffee-v1-svc +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: coffee-v1 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coffee-v2 +spec: + replicas: 2 + selector: + matchLabels: + app: coffee-v2 + template: + metadata: + labels: + app: coffee-v2 + spec: + containers: + - name: coffee-v2 + image: nginxdemos/hello:plain-text + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: coffee-v2-svc +spec: + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app: coffee-v2 \ No newline at end of file diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh new file mode 100755 index 0000000000..5ce58d122f --- /dev/null +++ b/hack/update-codegen.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# 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. + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/.. +CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${SCRIPT_ROOT}; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)} + +echo "$(dirname ${BASH_SOURCE})/../../.." + +# generate the code with: +# --output-base because this script should also be able to run inside the vendor dir of +# k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir +# instead of the $GOPATH directly. For normal projects this can be dropped. +${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \ + github.com/nginxinc/kubernetes-ingress/pkg/client github.com/nginxinc/kubernetes-ingress/pkg/apis \ + configuration:v1alpha1 \ + --go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt diff --git a/hack/verify-codegen.sh b/hack/verify-codegen.sh new file mode 100755 index 0000000000..d02a6fa395 --- /dev/null +++ b/hack/verify-codegen.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# 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. + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/.. + +DIFFROOT="${SCRIPT_ROOT}/pkg" +TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg" +_tmp="${SCRIPT_ROOT}/_tmp" + +cleanup() { + rm -rf "${_tmp}" +} +trap "cleanup" EXIT SIGINT + +cleanup + +mkdir -p "${TMP_DIFFROOT}" +cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" + +"${SCRIPT_ROOT}/hack/update-codegen.sh" +echo "diffing ${DIFFROOT} against freshly generated codegen" +ret=0 +diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? +cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}" +if [[ $ret -eq 0 ]] +then + echo "${DIFFROOT} up to date." +else + echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" + exit 1 +fi diff --git a/internal/configs/configurator.go b/internal/configs/configurator.go index 6b767d0717..23c9defae7 100644 --- a/internal/configs/configurator.go +++ b/internal/configs/configurator.go @@ -5,9 +5,12 @@ import ( "fmt" "strings" + "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" + "github.com/golang/glog" "github.com/nginxinc/kubernetes-ingress/internal/configs/version1" "github.com/nginxinc/kubernetes-ingress/internal/nginx" + conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" api_v1 "k8s.io/api/core/v1" extensions "k8s.io/api/extensions/v1beta1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -27,27 +30,30 @@ const JWTKeyKey = "jwk" // Configurator configures NGINX. type Configurator struct { - nginxManager nginx.Manager - staticCfgParams *StaticConfigParams - cfgParams *ConfigParams - templateExecutor *version1.TemplateExecutor - ingresses map[string]*IngressEx - minions map[string]map[string]bool - isWildcardEnabled bool - isPlus bool + nginxManager nginx.Manager + staticCfgParams *StaticConfigParams + cfgParams *ConfigParams + templateExecutor *version1.TemplateExecutor + templateExecutorV2 *version2.TemplateExecutor + ingresses map[string]*IngressEx + minions map[string]map[string]bool + isWildcardEnabled bool + isPlus bool } // NewConfigurator creates a new Configurator. -func NewConfigurator(nginxManager nginx.Manager, staticCfgParams *StaticConfigParams, config *ConfigParams, templateExecutor *version1.TemplateExecutor, isPlus bool, isWildcardEnabled bool) *Configurator { +func NewConfigurator(nginxManager nginx.Manager, staticCfgParams *StaticConfigParams, config *ConfigParams, templateExecutor *version1.TemplateExecutor, + templateExecutorV2 *version2.TemplateExecutor, isPlus bool, isWildcardEnabled bool) *Configurator { cnf := Configurator{ - nginxManager: nginxManager, - staticCfgParams: staticCfgParams, - cfgParams: config, - ingresses: make(map[string]*IngressEx), - templateExecutor: templateExecutor, - minions: make(map[string]map[string]bool), - isPlus: isPlus, - isWildcardEnabled: isWildcardEnabled, + nginxManager: nginxManager, + staticCfgParams: staticCfgParams, + cfgParams: config, + ingresses: make(map[string]*IngressEx), + templateExecutor: templateExecutor, + templateExecutorV2: templateExecutorV2, + minions: make(map[string]map[string]bool), + isPlus: isPlus, + isWildcardEnabled: isWildcardEnabled, } return &cnf } @@ -130,6 +136,37 @@ func (cnf *Configurator) addOrUpdateMergeableIngress(mergeableIngs *MergeableIng return nil } +// AddOrUpdateVirtualServer adds or updates NGINX configuration for the VirtualServer resource. +func (cnf *Configurator) AddOrUpdateVirtualServer(virtualServerEx *VirtualServerEx) error { + if err := cnf.addOrUpdateVirtualServer(virtualServerEx); err != nil { + return fmt.Errorf("Error adding or updating VirtualServer %v/%v: %v", virtualServerEx.VirtualServer.Namespace, virtualServerEx.VirtualServer.Name, err) + } + + if err := cnf.nginxManager.Reload(); err != nil { + return fmt.Errorf("Error reloading NGINX for VirtualServer %v/%v: %v", virtualServerEx.VirtualServer.Namespace, virtualServerEx.VirtualServer.Name, err) + } + + return nil +} + +func (cnf *Configurator) addOrUpdateVirtualServer(virtualServerEx *VirtualServerEx) error { + tlsPemFileName := "" + if virtualServerEx.TLSSecret != nil { + tlsPemFileName = cnf.addOrUpdateTLSSecret(virtualServerEx.TLSSecret) + } + + vsCfg := generateVirtualServerConfig(virtualServerEx, tlsPemFileName, cnf.cfgParams, cnf.isPlus) + + name := getFileNameForVirtualServer(virtualServerEx.VirtualServer) + content, err := cnf.templateExecutorV2.ExecuteVirtualServerTemplate(&vsCfg) + if err != nil { + return fmt.Errorf("Error generating VirtualServer config: %v: %v", name, err) + } + cnf.nginxManager.CreateConfig(name, content) + + return nil +} + func (cnf *Configurator) updateTLSSecrets(ingEx *IngressEx) map[string]string { pems := make(map[string]string) @@ -177,7 +214,7 @@ func (cnf *Configurator) AddOrUpdateJWKSecret(secret *api_v1.Secret) { } // AddOrUpdateTLSSecret adds or updates a file with the content of the TLS secret. -func (cnf *Configurator) AddOrUpdateTLSSecret(secret *api_v1.Secret, ingExes []IngressEx, mergeableIngresses []MergeableIngresses) error { +func (cnf *Configurator) AddOrUpdateTLSSecret(secret *api_v1.Secret, ingExes []IngressEx, mergeableIngresses []MergeableIngresses, virtualServerExes []*VirtualServerEx) error { cnf.addOrUpdateTLSSecret(secret) for i := range ingExes { @@ -194,6 +231,13 @@ func (cnf *Configurator) AddOrUpdateTLSSecret(secret *api_v1.Secret, ingExes []I } } + for _, vsEx := range virtualServerExes { + err := cnf.addOrUpdateVirtualServer(vsEx) + if err != nil { + return fmt.Errorf("Error adding or updating VirtualServer %v/%v: %v", vsEx.VirtualServer.Namespace, vsEx.VirtualServer.Name, err) + } + } + if err := cnf.nginxManager.Reload(); err != nil { return fmt.Errorf("Error when reloading NGINX when updating Secret: %v", err) } @@ -233,8 +277,9 @@ func GenerateCertAndKeyFileContent(secret *api_v1.Secret) []byte { return res.Bytes() } -// DeleteSecret deletes the file associated with the secret and the configuration files for the Ingress resources. NGINX is reloaded only when len(ings) > 0. -func (cnf *Configurator) DeleteSecret(key string, ingExes []IngressEx, mergeableIngresses []MergeableIngresses) error { +// DeleteSecret deletes the file associated with the secret and the configuration files for Ingress and VirtualServer resources. +// NGINX is reloaded only when the total number of the resources > 0. +func (cnf *Configurator) DeleteSecret(key string, ingExes []IngressEx, mergeableIngresses []MergeableIngresses, virtualServerExes []*VirtualServerEx) error { cnf.nginxManager.DeleteSecret(keyToFileName(key)) for i := range ingExes { @@ -251,7 +296,14 @@ func (cnf *Configurator) DeleteSecret(key string, ingExes []IngressEx, mergeable } } - if len(ingExes)+len(mergeableIngresses) > 0 { + for _, vsEx := range virtualServerExes { + err := cnf.addOrUpdateVirtualServer(vsEx) + if err != nil { + return fmt.Errorf("Error adding or updating VirtualServer %v/%v: %v", vsEx.VirtualServer.Namespace, vsEx.VirtualServer.Name, err) + } + } + + if len(ingExes)+len(mergeableIngresses)+len(virtualServerExes) > 0 { if err := cnf.nginxManager.Reload(); err != nil { return fmt.Errorf("Error when reloading NGINX when deleting Secret %v: %v", key, err) } @@ -275,6 +327,18 @@ func (cnf *Configurator) DeleteIngress(key string) error { return nil } +// DeleteVirtualServer deletes NGINX configuration for the VirtualServer resource. +func (cnf *Configurator) DeleteVirtualServer(key string) error { + name := getFileNameForVirtualServerFromKey(key) + cnf.nginxManager.DeleteConfig(name) + + if err := cnf.nginxManager.Reload(); err != nil { + return fmt.Errorf("Error when removing VirtualServer %v: %v", key, err) + } + + return nil +} + // UpdateEndpoints updates endpoints in NGINX configuration for the Ingress resources. func (cnf *Configurator) UpdateEndpoints(ingExes []*IngressEx) error { reloadPlus := false @@ -339,6 +403,51 @@ func (cnf *Configurator) UpdateEndpointsMergeableIngress(mergeableIngresses []*M return nil } +// UpdateEndpointsForVirtualServers updates endpoints in NGINX configuration for the s resources. +func (cnf *Configurator) UpdateEndpointsForVirtualServers(virtualServerExes []*VirtualServerEx) error { + reloadPlus := false + + for _, vs := range virtualServerExes { + err := cnf.addOrUpdateVirtualServer(vs) + if err != nil { + return fmt.Errorf("Error adding or updating VirtualServer %v/%v: %v", vs.VirtualServer.Namespace, vs.VirtualServer.Name, err) + } + + if cnf.isPlus { + err := cnf.updatePlusEndpointsForVirtualServer(vs) + if err != nil { + glog.Warningf("Couldn't update the endpoints via the API: %v; reloading configuration instead", err) + reloadPlus = true + } + } + } + + if cnf.isPlus && !reloadPlus { + glog.V(3).Info("No need to reload nginx") + return nil + } + + if err := cnf.nginxManager.Reload(); err != nil { + return fmt.Errorf("Error reloading NGINX when updating endpoints: %v", err) + } + + return nil +} + +func (cnf *Configurator) updatePlusEndpointsForVirtualServer(virtualServerEx *VirtualServerEx) error { + serverCfg := createUpstreamServersConfig(cnf.cfgParams) + upstreamServers := createUpstreamServersForPlus(virtualServerEx) + + for upstream, servers := range upstreamServers { + err := cnf.nginxManager.UpdateServersInPlus(upstream, servers, serverCfg) + if err != nil { + return fmt.Errorf("Couldn't update the endpoints for %v: %v", upstream, err) + } + } + + return nil +} + func (cnf *Configurator) updatePlusEndpoints(ingEx *IngressEx) error { ingCfg := parseAnnotations(ingEx, cnf.cfgParams, cnf.isPlus) @@ -389,7 +498,7 @@ func (cnf *Configurator) updatePlusEndpoints(ingEx *IngressEx) error { } // UpdateConfig updates NGINX configuration parameters. -func (cnf *Configurator) UpdateConfig(cfgParams *ConfigParams, ingExes []*IngressEx, mergeableIngs map[string]*MergeableIngresses) error { +func (cnf *Configurator) UpdateConfig(cfgParams *ConfigParams, ingExes []*IngressEx, mergeableIngs map[string]*MergeableIngresses, virtualServerExes []*VirtualServerEx) error { cnf.cfgParams = cfgParams if cnf.cfgParams.MainServerSSLDHParamFileContent != nil { @@ -431,6 +540,11 @@ func (cnf *Configurator) UpdateConfig(cfgParams *ConfigParams, ingExes []*Ingres return err } } + for _, vsEx := range virtualServerExes { + if err := cnf.addOrUpdateVirtualServer(vsEx); err != nil { + return err + } + } if err := cnf.nginxManager.Reload(); err != nil { return fmt.Errorf("Error when updating config from ConfigMap: %v", err) @@ -447,6 +561,15 @@ func objectMetaToFileName(meta *meta_v1.ObjectMeta) string { return meta.Namespace + "-" + meta.Name } +func getFileNameForVirtualServer(virtualServer *conf_v1alpha1.VirtualServer) string { + return fmt.Sprintf("vs_%s_%s", virtualServer.Namespace, virtualServer.Name) +} + +func getFileNameForVirtualServerFromKey(key string) string { + replaced := strings.Replace(key, "/", "_", -1) + return fmt.Sprintf("vs_%s", replaced) +} + // HasIngress checks if the Ingress resource is present in NGINX configuration. func (cnf *Configurator) HasIngress(ing *extensions.Ingress) bool { name := objectMetaToFileName(&ing.ObjectMeta) diff --git a/internal/configs/configurator_test.go b/internal/configs/configurator_test.go index 10a0299e44..3c4a15c451 100644 --- a/internal/configs/configurator_test.go +++ b/internal/configs/configurator_test.go @@ -3,8 +3,12 @@ package configs import ( "testing" + "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" + "github.com/nginxinc/kubernetes-ingress/internal/configs/version1" "github.com/nginxinc/kubernetes-ingress/internal/nginx" + conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func createTestStaticConfigParams() *StaticConfigParams { @@ -23,9 +27,14 @@ func createTestConfigurator() (*Configurator, error) { return nil, err } + templateExecutorV2, err := version2.NewTemplateExecutor("version2/nginx-plus.virtualserver.tmpl") + if err != nil { + return nil, err + } + manager := nginx.NewFakeManager("/etc/nginx") - return NewConfigurator(manager, createTestStaticConfigParams(), NewDefaultConfigParams(), templateExecutor, false, false), nil + return NewConfigurator(manager, createTestStaticConfigParams(), NewDefaultConfigParams(), templateExecutor, templateExecutorV2, false, false), nil } func createTestConfiguratorInvalidIngressTemplate() (*Configurator, error) { @@ -41,7 +50,7 @@ func createTestConfiguratorInvalidIngressTemplate() (*Configurator, error) { manager := nginx.NewFakeManager("/etc/nginx") - return NewConfigurator(manager, createTestStaticConfigParams(), NewDefaultConfigParams(), templateExecutor, false, false), nil + return NewConfigurator(manager, createTestStaticConfigParams(), NewDefaultConfigParams(), templateExecutor, &version2.TemplateExecutor{}, false, false), nil } func TestAddOrUpdateIngress(t *testing.T) { @@ -179,3 +188,30 @@ func TestUpdateEndpointsMergeableIngressFailsWithInvalidTemplate(t *testing.T) { t.Errorf("UpdateEndpointsMergeableIngress returned \n%v, but expected \n%v", nil, "template execution error") } } + +func TestGetVirtualServerConfigFileName(t *testing.T) { + vs := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "test", + Name: "virtual-server", + }, + } + + expected := "vs_test_virtual-server" + + result := getFileNameForVirtualServer(&vs) + if result != expected { + t.Errorf("getFileNameForVirtualServer returned %v, but expected %v", result, expected) + } +} + +func TestGetFileNameForVirtualServerFromKey(t *testing.T) { + key := "default/cafe" + + expected := "vs_default_cafe" + + result := getFileNameForVirtualServerFromKey(key) + if result != expected { + t.Errorf("getFileNameForVirtualServerFromKey returned %v, but expected %v", result, expected) + } +} diff --git a/internal/configs/version1/nginx-plus.tmpl b/internal/configs/version1/nginx-plus.tmpl index 032bf528f2..89d1ecbc2f 100644 --- a/internal/configs/version1/nginx-plus.tmpl +++ b/internal/configs/version1/nginx-plus.tmpl @@ -56,6 +56,8 @@ http { server_names_hash_max_size {{.ServerNamesHashMaxSize}}; {{if .ServerNamesHashBucketSize}}server_names_hash_bucket_size {{.ServerNamesHashBucketSize}};{{end}} + variables_hash_bucket_size 256; + map $http_upgrade $connection_upgrade { default upgrade; '' close; diff --git a/internal/configs/version1/nginx.tmpl b/internal/configs/version1/nginx.tmpl index 01a8d2460f..9621836e60 100644 --- a/internal/configs/version1/nginx.tmpl +++ b/internal/configs/version1/nginx.tmpl @@ -55,6 +55,8 @@ http { server_names_hash_max_size {{.ServerNamesHashMaxSize}}; {{if .ServerNamesHashBucketSize}}server_names_hash_bucket_size {{.ServerNamesHashBucketSize}};{{end}} + variables_hash_bucket_size 256; + map $http_upgrade $connection_upgrade { default upgrade; '' close; @@ -113,6 +115,15 @@ http { include /etc/nginx/config-version.conf; include /etc/nginx/conf.d/*.conf; + + server { + listen unix:/var/run/nginx-502-server.sock; + access_log off; + + location / { + return 502; + } + } } stream { diff --git a/internal/configs/version2/config.go b/internal/configs/version2/config.go new file mode 100644 index 0000000000..a477abe6b8 --- /dev/null +++ b/internal/configs/version2/config.go @@ -0,0 +1,94 @@ +package version2 + +// VirtualServerConfig holds NGINX configuration for a VirtualServer. +type VirtualServerConfig struct { + Server Server + Upstreams []Upstream + SplitClients []SplitClient + Maps []Map + Keepalive string +} + +// Upstream defines an upstream. +type Upstream struct { + Name string + Servers []UpstreamServer + LBMethod string +} + +// UpstreamServer defines an upstream server. +type UpstreamServer struct { + Address string + MaxFails int + FailTimeout string +} + +// Server defines a server. +type Server struct { + ServerName string + ProxyProtocol bool + SSL *SSL + RedirectToHTTPSBasedOnXForwarderProto bool + ServerTokens string + RealIPHeader string + SetRealIPFrom []string + RealIPRecursive bool + Snippets []string + InternalRedirectLocations []InternalRedirectLocation + Locations []Location +} + +// SSL defines SSL configuration for a server. +type SSL struct { + HTTP2 bool + Certificate string + CertificateKey string + Ciphers string + RedirectToHTTPS bool +} + +// Location defines a location. +type Location struct { + Path string + Snippets []string + ProxyConnectTimeout string + ProxyReadTimeout string + ClientMaxBodySize string + ProxyMaxTempFileSize string + ProxyBuffering bool + ProxyBuffers string + ProxyBufferSize string + ProxyPass string +} + +// SplitClient defines a split_clients. +type SplitClient struct { + Source string + Variable string + Distributions []Distribution +} + +// Distribution maps weight to a value in a SplitClient. +type Distribution struct { + Weight string + Value string +} + +// InternalRedirectLocation defines a location for internally redirecting requests to named locations. +type InternalRedirectLocation struct { + Path string + Destination string +} + +// Map defines a map. +type Map struct { + Source string + Variable string + Parameters []Parameter +} + +// Parameter defines a Parameter in a Map. +type Parameter struct { + Value string + Result string +} diff --git a/internal/configs/version2/nginx-plus.virtualserver.tmpl b/internal/configs/version2/nginx-plus.virtualserver.tmpl new file mode 100644 index 0000000000..56a9deef52 --- /dev/null +++ b/internal/configs/version2/nginx-plus.virtualserver.tmpl @@ -0,0 +1,123 @@ +{{ range $u := .Upstreams }} +upstream {{ $u.Name }} { + zone {{ $u.Name }} 256k; + + {{ if $u.LBMethod }}{{ $u.LBMethod }};{{ end }} + + {{ range $s := $u.Servers }} + server {{ $s.Address }} max_fails={{ $s.MaxFails }} fail_timeout={{ $s.FailTimeout }}; + {{ end }} + + {{ if $.Keepalive }} + keepalive {{ $.Keepalive }}; + {{ end }} +} +{{ end }} + +{{ range $sc := .SplitClients }} +split_clients {{ $sc.Source }} {{ $sc.Variable }} { + {{ range $d := $sc.Distributions }} + {{ $d.Weight }} {{ $d.Value }}; + {{ end }} +} +{{ end }} + +{{ range $m := .Maps }} +map {{ $m.Source }} {{ $m.Variable }} { + {{ range $p := $m.Parameters }} + {{ $p.Value }} {{ $p.Result }}; + {{ end }} +} +{{ end }} + +{{ $s := .Server }} +server { + listen 80{{ if $s.ProxyProtocol }} proxy_protocol{{ end }}; + + server_name {{ $s.ServerName }}; + + {{ with $ssl := $s.SSL }} + listen 443 ssl{{ if $ssl.HTTP2 }} http2{{ end }}{{ if $s.ProxyProtocol }} proxy_protocol{{ end }}; + + ssl_certificate {{ $ssl.Certificate }}; + ssl_certificate_key {{ $ssl.CertificateKey }}; + + {{ if $ssl.Ciphers }} + ssl_ciphers {{ $ssl.Ciphers }}; + {{ end }} + + {{ if $ssl.RedirectToHTTPS }} + if ($scheme = http) { + return 301 https://$host$request_uri; + } + {{ end }} + {{ end }} + + {{ if $s.RedirectToHTTPSBasedOnXForwarderProto }} + if ($http_x_forwarded_proto = 'http') { + return 301 https://$host$request_uri; + } + {{ end }} + + server_tokens "{{ $s.ServerTokens }}"; + + {{ range $setRealIPFrom := $s.SetRealIPFrom }} + set_real_ip_from {{ $setRealIPFrom }}; + {{ end }} + {{ if $s.RealIPHeader }} + real_ip_header {{ $s.RealIPHeader }}; + {{ end }} + {{ if $s.RealIPRecursive }} + real_ip_recursive on; + {{ end }} + + {{ range $snippet := $s.Snippets }} + {{ $snippet }} + {{ end }} + + {{ range $l := $s.InternalRedirectLocations }} + location {{ $l.Path }} { + error_page 418 = {{ $l.Destination }}; + return 418; + } + {{ end }} + + {{ range $l := $s.Locations }} + location {{ $l.Path }} { + {{ range $snippet := $l.Snippets }} + {{ $snippet }} + {{ end }} + + proxy_connect_timeout {{ $l.ProxyConnectTimeout }}; + proxy_read_timeout {{ $l.ProxyReadTimeout }}; + client_max_body_size {{ $l.ClientMaxBodySize }}; + + {{ if $l.ProxyMaxTempFileSize }} + proxy_max_temp_file_size {{ $l.ProxyMaxTempFileSize }}; + {{ end }} + + proxy_buffering {{ if $l.ProxyBuffering }}on{{ else }}off{{ end }}; + {{ if $l.ProxyBuffers }} + proxy_buffers {{ $l.ProxyBuffers }}; + {{ end }} + {{ if $l.ProxyBufferSize }} + proxy_buffer_size {{ $l.ProxyBufferSize }}; + {{ end }} + + proxy_http_version 1.1; + + {{ if $.Keepalive }} + proxy_set_header Connection ""; + {{ end }} + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_pass {{ $l.ProxyPass }}; + } + {{ end }} +} \ No newline at end of file diff --git a/internal/configs/version2/nginx.virtualserver.tmpl b/internal/configs/version2/nginx.virtualserver.tmpl new file mode 100644 index 0000000000..456f41a4d1 --- /dev/null +++ b/internal/configs/version2/nginx.virtualserver.tmpl @@ -0,0 +1,123 @@ +{{ range $u := .Upstreams }} +upstream {{ $u.Name }} { + zone {{ $u.Name }} 256k; + + {{ if $u.LBMethod }}{{ $u.LBMethod }};{{ end }} + + {{ range $s := $u.Servers }} + server {{ $s.Address }} max_fails={{ $s.MaxFails }} fail_timeout={{ $s.FailTimeout }}; + {{ end }} + + {{ if $.Keepalive }} + keepalive {{ $.Keepalive }}; + {{ end }} +} +{{ end }} + +{{ range $sc := .SplitClients }} +split_clients {{ $sc.Source }} {{ $sc.Variable }} { + {{ range $d := $sc.Distributions }} + {{ $d.Weight }} {{ $d.Value }}; + {{ end }} +} +{{ end }} + +{{ range $m := .Maps }} +map {{ $m.Source }} {{ $m.Variable }} { + {{ range $p := $m.Parameters }} + {{ $p.Value }} {{ $p.Result }}; + {{ end }} +} +{{ end }} + +{{ $s := .Server }} +server { + listen 80{{ if $s.ProxyProtocol }} proxy_protocol{{ end }}; + + server_name {{ $s.ServerName }}; + + {{ with $ssl := $s.SSL }} + listen 443 ssl{{ if $ssl.HTTP2 }} http2{{ end }}{{ if $s.ProxyProtocol }} proxy_protocol{{ end }}; + + ssl_certificate {{ $ssl.Certificate }}; + ssl_certificate_key {{ $ssl.CertificateKey }}; + + {{ if $ssl.Ciphers }} + ssl_ciphers {{ $ssl.Ciphers }}; + {{ end }} + + {{ if $ssl.RedirectToHTTPS }} + if ($scheme = http) { + return 301 https://$host$request_uri; + } + {{ end }} + {{ end }} + + {{ if $s.RedirectToHTTPSBasedOnXForwarderProto }} + if ($http_x_forwarded_proto = 'http') { + return 301 https://$host$request_uri; + } + {{ end }} + + server_tokens "{{ $s.ServerTokens }}"; + + {{ range $setRealIPFrom := $s.SetRealIPFrom }} + set_real_ip_from {{ $setRealIPFrom }}; + {{ end }} + {{ if $s.RealIPHeader }} + real_ip_header {{ $s.RealIPHeader }}; + {{ end }} + {{ if $s.RealIPRecursive }} + real_ip_recursive on; + {{ end }} + + {{ range $snippet := $s.Snippets }} + {{ $snippet }} + {{ end }} + + {{ range $l := $s.InternalRedirectLocations }} + location {{ $l.Path }} { + error_page 418 = {{ $l.Destination }}; + return 418; + } + {{ end }} + + {{ range $l := $s.Locations }} + location {{ $l.Path }} { + {{ range $snippet := $l.Snippets }} + {{ $snippet }} + {{ end }} + + proxy_connect_timeout {{ $l.ProxyConnectTimeout }}; + proxy_read_timeout {{ $l.ProxyReadTimeout }}; + client_max_body_size {{ $l.ClientMaxBodySize }}; + + {{ if $l.ProxyMaxTempFileSize }} + proxy_max_temp_file_size {{ $l.ProxyMaxTempFileSize }}; + {{ end }} + + proxy_buffering {{ if $l.ProxyBuffering }}on{{ else }}off{{ end }}; + {{ if $l.ProxyBuffers }} + proxy_buffers {{ $l.ProxyBuffers }}; + {{ end }} + {{ if $l.ProxyBufferSize }} + proxy_buffer_size {{ $l.ProxyBufferSize }}; + {{ end }} + + proxy_http_version 1.1; + + {{ if $.Keepalive }} + proxy_set_header Connection ""; + {{ end }} + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_pass {{ $l.ProxyPass }}; + } + {{ end }} +} \ No newline at end of file diff --git a/internal/configs/version2/template_executor.go b/internal/configs/version2/template_executor.go new file mode 100644 index 0000000000..fb34d9a017 --- /dev/null +++ b/internal/configs/version2/template_executor.go @@ -0,0 +1,33 @@ +package version2 + +import ( + "bytes" + "path" + "text/template" +) + +// TemplateExecutor executes NGINX configuration templates. +type TemplateExecutor struct { + virtualServerTemplate *template.Template +} + +// NewTemplateExecutor creates a TemplateExecutor. +func NewTemplateExecutor(virtualServerTemplatePath string) (*TemplateExecutor, error) { + // template name must be the base name of the template file https://golang.org/pkg/text/template/#Template.ParseFiles + vsTemplate, err := template.New(path.Base(virtualServerTemplatePath)).ParseFiles(virtualServerTemplatePath) + if err != nil { + return nil, err + } + + return &TemplateExecutor{ + virtualServerTemplate: vsTemplate, + }, nil +} + +// ExecuteVirtualServerTemplate generates the content of an NGINX configuration file for a VirtualServer resource. +func (te *TemplateExecutor) ExecuteVirtualServerTemplate(cfg *VirtualServerConfig) ([]byte, error) { + var configBuffer bytes.Buffer + err := te.virtualServerTemplate.Execute(&configBuffer, cfg) + + return configBuffer.Bytes(), err +} diff --git a/internal/configs/version2/templates_test.go b/internal/configs/version2/templates_test.go new file mode 100644 index 0000000000..256a11ddb2 --- /dev/null +++ b/internal/configs/version2/templates_test.go @@ -0,0 +1,186 @@ +package version2 + +import "testing" + +const nginxPlusVirtualServerTmpl = "nginx-plus.virtualserver.tmpl" +const nginxVirtualServerTmpl = "nginx.virtualserver.tmpl" + +var virtualServerCfg = VirtualServerConfig{ + Upstreams: []Upstream{ + { + Name: "test-upstream", + Servers: []UpstreamServer{ + { + Address: "10.0.0.20:8001", + MaxFails: 5, + FailTimeout: "10s", + }, + }, + LBMethod: "random", + }, + { + Name: "coffee-v1", + Servers: []UpstreamServer{ + { + Address: "10.0.0.31:8001", + MaxFails: 5, + FailTimeout: "10s", + }, + }, + }, + { + Name: "coffee-v2", + Servers: []UpstreamServer{ + { + Address: "10.0.0.32:8001", + MaxFails: 5, + FailTimeout: "10s", + }, + }, + }, + }, + SplitClients: []SplitClient{ + { + Source: "$request_id", + Variable: "$split_0", + Distributions: []Distribution{ + { + Weight: "50%", + Value: "@loc0", + }, + { + Weight: "50%", + Value: "@loc1", + }, + }, + }, + }, + Maps: []Map{ + { + Source: "$match_0_0", + Variable: "$match", + Parameters: []Parameter{ + { + Value: "~^1", + Result: "@match_loc_0", + }, + { + Value: "default", + Result: "@match_loc_default", + }, + }, + }, + { + Source: "$http_x_version", + Variable: "$match_0_0", + Parameters: []Parameter{ + { + Value: "v2", + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + }, + Server: Server{ + ServerName: "example.com", + ProxyProtocol: true, + SSL: &SSL{ + HTTP2: true, + Certificate: "cafe-secret.pem", + CertificateKey: "cafe-secret.pem", + Ciphers: "NULL", + RedirectToHTTPS: true, + }, + RedirectToHTTPSBasedOnXForwarderProto: true, + ServerTokens: "off", + SetRealIPFrom: []string{"0.0.0.0/0"}, + RealIPHeader: "X-Real-IP", + RealIPRecursive: true, + Snippets: []string{"# server snippet"}, + InternalRedirectLocations: []InternalRedirectLocation{ + { + Path: "/split", + Destination: "@split_0", + }, + { + Path: "/coffee", + Destination: "@match", + }, + }, + Locations: []Location{ + { + Path: "/", + Snippets: []string{"# location snippet"}, + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ClientMaxBodySize: "1m", + ProxyBuffering: true, + ProxyBuffers: "8 4k", + ProxyBufferSize: "4k", + ProxyMaxTempFileSize: "1024m", + ProxyPass: "http://test-upstream", + }, + { + Path: "@loc0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + }, + { + Path: "@loc1", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + }, + { + Path: "@match_loc_0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + }, + { + Path: "@match_loc_default", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + }, + }, + }, + Keepalive: "10", +} + +func TestVirtualServerForNginxPlus(t *testing.T) { + executor, err := NewTemplateExecutor(nginxPlusVirtualServerTmpl) + if err != nil { + t.Fatalf("Failed to create template executor: %v", err) + } + + data, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfg) + if err != nil { + t.Fatalf("Failed to execute template: %v", err) + } + + t.Log(string(data)) +} + +func TestVirtualServerForNginx(t *testing.T) { + executor, err := NewTemplateExecutor(nginxVirtualServerTmpl) + if err != nil { + t.Fatalf("Failed to create template executor: %v", err) + } + + data, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfg) + if err != nil { + t.Fatalf("Failed to execute template: %v", err) + } + + t.Log(string(data)) +} diff --git a/internal/configs/virtualserver.go b/internal/configs/virtualserver.go new file mode 100644 index 0000000000..9fe954fd82 --- /dev/null +++ b/internal/configs/virtualserver.go @@ -0,0 +1,507 @@ +package configs + +import ( + "fmt" + "strings" + + "github.com/nginxinc/kubernetes-ingress/internal/nginx" + api_v1 "k8s.io/api/core/v1" + + "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" + conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" +) + +const nginx502Server = "unix:/var/run/nginx-502-server.sock" + +// VirtualServerEx holds a VirtualServer along with the resources that are referenced in this VirtualServer. +type VirtualServerEx struct { + VirtualServer *conf_v1alpha1.VirtualServer + Endpoints map[string][]string + TLSSecret *api_v1.Secret + VirtualServerRoutes []*conf_v1alpha1.VirtualServerRoute +} + +func (vsx *VirtualServerEx) String() string { + if vsx == nil { + return "" + } + + if vsx.VirtualServer == nil { + return "VirtualServerEx has no VirtualServer" + } + + return fmt.Sprintf("%s/%s", vsx.VirtualServer.Namespace, vsx.VirtualServer.Name) +} + +// GenerateEndpointsKey generates a key for the Endpoints map in VirtualServerEx. +func GenerateEndpointsKey(serviceNamespace string, serviceName string, port uint16) string { + return fmt.Sprintf("%s/%s:%d", serviceNamespace, serviceName, port) +} + +type upstreamNamer struct { + prefix string +} + +func newUpstreamNamerForVirtualServer(virtualServer *conf_v1alpha1.VirtualServer) *upstreamNamer { + return &upstreamNamer{ + prefix: fmt.Sprintf("vs_%s_%s", virtualServer.Namespace, virtualServer.Name), + } +} + +func newUpstreamNamerForVirtualServerRoute(virtualServer *conf_v1alpha1.VirtualServer, virtualServerRoute *conf_v1alpha1.VirtualServerRoute) *upstreamNamer { + return &upstreamNamer{ + prefix: fmt.Sprintf("vs_%s_%s_vsr_%s_%s", virtualServer.Namespace, virtualServer.Name, virtualServerRoute.Namespace, virtualServerRoute.Name), + } +} + +func (namer *upstreamNamer) GetNameForUpstream(upstream string) string { + return fmt.Sprintf("%s_%s", namer.prefix, upstream) +} + +type variableNamer struct { + safeNsName string +} + +func newVariableNamer(virtualServer *conf_v1alpha1.VirtualServer) *variableNamer { + safeNsName := strings.ReplaceAll(fmt.Sprintf("%s_%s", virtualServer.Namespace, virtualServer.Name), "-", "_") + return &variableNamer{ + safeNsName: safeNsName, + } +} + +func (namer *variableNamer) GetNameForSplitClientVariable(index int) string { + return fmt.Sprintf("$vs_%s_splits_%d", namer.safeNsName, index) +} + +func (namer *variableNamer) GetNameForVariableForRulesRouteMap(rulesIndex int, matchIndex int, conditionIndex int) string { + return fmt.Sprintf("$vs_%s_rules_%d_match_%d_cond_%d", namer.safeNsName, rulesIndex, matchIndex, conditionIndex) +} + +func (namer *variableNamer) GetNameForVariableForRulesRouteMainMap(rulesIndex int) string { + return fmt.Sprintf("$vs_%s_rules_%d", namer.safeNsName, rulesIndex) +} + +func generateVirtualServerConfig(virtualServerEx *VirtualServerEx, tlsPemFileName string, baseCfgParams *ConfigParams, isPlus bool) version2.VirtualServerConfig { + ssl := generateSSLConfig(virtualServerEx.VirtualServer.Spec.TLS, tlsPemFileName, baseCfgParams) + + virtualServerUpstreamNamer := newUpstreamNamerForVirtualServer(virtualServerEx.VirtualServer) + + var upstreams []version2.Upstream + + // generate upstreams for VirtualServer + for _, u := range virtualServerEx.VirtualServer.Spec.Upstreams { + upstreamName := virtualServerUpstreamNamer.GetNameForUpstream(u.Name) + endpointsKey := GenerateEndpointsKey(virtualServerEx.VirtualServer.Namespace, u.Service, u.Port) + ups := generateUpstream(upstreamName, virtualServerEx.Endpoints[endpointsKey], isPlus, baseCfgParams) + upstreams = append(upstreams, ups) + } + // generate upstreams for each VirtualServerRoute + for _, vsr := range virtualServerEx.VirtualServerRoutes { + upstreamNamer := newUpstreamNamerForVirtualServerRoute(virtualServerEx.VirtualServer, vsr) + for _, u := range vsr.Spec.Upstreams { + upstreamName := upstreamNamer.GetNameForUpstream(u.Name) + endpointsKey := GenerateEndpointsKey(vsr.Namespace, u.Service, u.Port) + ups := generateUpstream(upstreamName, virtualServerEx.Endpoints[endpointsKey], isPlus, baseCfgParams) + upstreams = append(upstreams, ups) + } + } + + var locations []version2.Location + var internalRedirectLocations []version2.InternalRedirectLocation + var splitClients []version2.SplitClient + var maps []version2.Map + + rulesRoutes := 0 + + variableNamer := newVariableNamer(virtualServerEx.VirtualServer) + + // generates config for VirtualServer routes + for _, r := range virtualServerEx.VirtualServer.Spec.Routes { + // ignore routes that reference VirtualServerRoute + if r.Route != "" { + continue + } + + if len(r.Splits) > 0 { + splitCfg := generateSplitRouteConfig(r, virtualServerUpstreamNamer, variableNamer, len(splitClients), baseCfgParams) + + splitClients = append(splitClients, splitCfg.SplitClient) + locations = append(locations, splitCfg.Locations...) + internalRedirectLocations = append(internalRedirectLocations, splitCfg.InternalRedirectLocation) + } else if r.Rules != nil { + rulesRouteCfg := generateRulesRouteConfig(r, virtualServerUpstreamNamer, variableNamer, rulesRoutes, baseCfgParams) + + maps = append(maps, rulesRouteCfg.Maps...) + locations = append(locations, rulesRouteCfg.Locations...) + internalRedirectLocations = append(internalRedirectLocations, rulesRouteCfg.InternalRedirectLocation) + + rulesRoutes++ + } else { + upstreamName := virtualServerUpstreamNamer.GetNameForUpstream(r.Upstream) + loc := generateLocation(r.Path, upstreamName, baseCfgParams) + locations = append(locations, loc) + } + } + + // generate config for subroutes of each VirtualServerRoute + for _, vsr := range virtualServerEx.VirtualServerRoutes { + upstreamNamer := newUpstreamNamerForVirtualServerRoute(virtualServerEx.VirtualServer, vsr) + for _, r := range vsr.Spec.Subroutes { + if len(r.Splits) > 0 { + splitCfg := generateSplitRouteConfig(r, upstreamNamer, variableNamer, len(splitClients), baseCfgParams) + + splitClients = append(splitClients, splitCfg.SplitClient) + locations = append(locations, splitCfg.Locations...) + internalRedirectLocations = append(internalRedirectLocations, splitCfg.InternalRedirectLocation) + } else if r.Rules != nil { + rulesRouteCfg := generateRulesRouteConfig(r, upstreamNamer, variableNamer, rulesRoutes, baseCfgParams) + + maps = append(maps, rulesRouteCfg.Maps...) + locations = append(locations, rulesRouteCfg.Locations...) + internalRedirectLocations = append(internalRedirectLocations, rulesRouteCfg.InternalRedirectLocation) + + rulesRoutes++ + } else { + upstreamName := upstreamNamer.GetNameForUpstream(r.Upstream) + loc := generateLocation(r.Path, upstreamName, baseCfgParams) + locations = append(locations, loc) + } + } + } + + keepalive := "" + if baseCfgParams.Keepalive > 0 { + keepalive = fmt.Sprint(baseCfgParams.Keepalive) + } + + return version2.VirtualServerConfig{ + Upstreams: upstreams, + SplitClients: splitClients, + Maps: maps, + Server: version2.Server{ + ServerName: virtualServerEx.VirtualServer.Spec.Host, + ProxyProtocol: baseCfgParams.ProxyProtocol, + SSL: ssl, + RedirectToHTTPSBasedOnXForwarderProto: baseCfgParams.RedirectToHTTPS, + ServerTokens: baseCfgParams.ServerTokens, + SetRealIPFrom: baseCfgParams.SetRealIPFrom, + RealIPHeader: baseCfgParams.RealIPHeader, + RealIPRecursive: baseCfgParams.RealIPRecursive, + Snippets: baseCfgParams.ServerSnippets, + InternalRedirectLocations: internalRedirectLocations, + Locations: locations, + }, + Keepalive: keepalive, + } +} + +func generateUpstream(upstreamName string, endpoints []string, isPlus bool, cfgParams *ConfigParams) version2.Upstream { + var upsServers []version2.UpstreamServer + + for _, e := range endpoints { + s := version2.UpstreamServer{ + Address: e, + MaxFails: cfgParams.MaxFails, + FailTimeout: cfgParams.FailTimeout, + } + upsServers = append(upsServers, s) + } + + if !isPlus && len(upsServers) == 0 { + s := version2.UpstreamServer{ + Address: nginx502Server, + MaxFails: cfgParams.MaxFails, + FailTimeout: cfgParams.FailTimeout, + } + upsServers = append(upsServers, s) + } + + ups := version2.Upstream{ + Name: upstreamName, + Servers: upsServers, + LBMethod: cfgParams.LBMethod, + } + + return ups +} + +func generateLocation(path string, upstreamName string, cfgParams *ConfigParams) version2.Location { + loc := version2.Location{ + Path: path, + Snippets: cfgParams.LocationSnippets, + ProxyConnectTimeout: cfgParams.ProxyConnectTimeout, + ProxyReadTimeout: cfgParams.ProxyReadTimeout, + ClientMaxBodySize: cfgParams.ClientMaxBodySize, + ProxyMaxTempFileSize: cfgParams.ProxyMaxTempFileSize, + ProxyBuffering: cfgParams.ProxyBuffering, + ProxyBuffers: cfgParams.ProxyBuffers, + ProxyBufferSize: cfgParams.ProxyBufferSize, + ProxyPass: fmt.Sprintf("http://%v", upstreamName), + } + return loc +} + +type splitRouteCfg struct { + SplitClient version2.SplitClient + Locations []version2.Location + InternalRedirectLocation version2.InternalRedirectLocation +} + +func generateSplitRouteConfig(route conf_v1alpha1.Route, upstreamNamer *upstreamNamer, variableNamer *variableNamer, index int, cfgParams *ConfigParams) splitRouteCfg { + splitClientVarName := variableNamer.GetNameForSplitClientVariable(index) + + // Generate a SplitClient + var distributions []version2.Distribution + + for i, s := range route.Splits { + d := version2.Distribution{ + Weight: fmt.Sprintf("%d%%", s.Weight), + Value: fmt.Sprintf("@splits_%d_split_%d", index, i), + } + distributions = append(distributions, d) + } + + splitClient := version2.SplitClient{ + Source: "$request_id", + Variable: splitClientVarName, + Distributions: distributions, + } + + // Generate locations + var locations []version2.Location + + for i, s := range route.Splits { + path := fmt.Sprintf("@splits_%d_split_%d", index, i) + upstreamName := upstreamNamer.GetNameForUpstream(s.Upstream) + loc := generateLocation(path, upstreamName, cfgParams) + locations = append(locations, loc) + } + + // Generate an InternalRedirectLocation + irl := version2.InternalRedirectLocation{ + Path: route.Path, + Destination: splitClientVarName, + } + + return splitRouteCfg{ + SplitClient: splitClient, + Locations: locations, + InternalRedirectLocation: irl, + } +} + +type rulesRouteCfg struct { + Maps []version2.Map + Locations []version2.Location + InternalRedirectLocation version2.InternalRedirectLocation +} + +func generateRulesRouteConfig(route conf_v1alpha1.Route, upstreamNamer *upstreamNamer, variableNamer *variableNamer, index int, cfgParams *ConfigParams) rulesRouteCfg { + // Generate maps + var maps []version2.Map + + for i, m := range route.Rules.Matches { + for j, c := range route.Rules.Conditions { + source := getNameForSourceForRulesRouteMapFromCondition(c) + variable := variableNamer.GetNameForVariableForRulesRouteMap(index, i, j) + successfulResult := "1" + if j < len(m.Values)-1 { + successfulResult = variableNamer.GetNameForVariableForRulesRouteMap(index, i, j+1) + } + + params := generateParametersForRulesRouteMap(m.Values[j], successfulResult) + + matchMap := version2.Map{ + Source: source, + Variable: variable, + Parameters: params, + } + maps = append(maps, matchMap) + } + } + + // Generate the main map + source := "" + var params []version2.Parameter + for i := range route.Rules.Matches { + source += variableNamer.GetNameForVariableForRulesRouteMap(index, i, 0) + + p := version2.Parameter{ + Value: fmt.Sprintf("~^%s1", strings.Repeat("0", i)), + Result: fmt.Sprintf("@rules_%d_match_%d", index, i), + } + params = append(params, p) + } + + defaultParam := version2.Parameter{ + Value: "default", + Result: fmt.Sprintf("@rules_%d_default", index), + } + params = append(params, defaultParam) + + variable := variableNamer.GetNameForVariableForRulesRouteMainMap(index) + + mainMap := version2.Map{ + Source: source, + Variable: variable, + Parameters: params, + } + maps = append(maps, mainMap) + + // Generate locations for each match + var locations []version2.Location + + for i, m := range route.Rules.Matches { + path := fmt.Sprintf("@rules_%d_match_%d", index, i) + upstreamName := upstreamNamer.GetNameForUpstream(m.Upstream) + loc := generateLocation(path, upstreamName, cfgParams) + locations = append(locations, loc) + } + + // Generate defaultUpsteam location + path := fmt.Sprintf("@rules_%d_default", index) + upstreamName := upstreamNamer.GetNameForUpstream(route.Rules.DefaultUpstream) + loc := generateLocation(path, upstreamName, cfgParams) + locations = append(locations, loc) + + // Generate an InternalRedirectLocation to the location defined by the main map variable + irl := version2.InternalRedirectLocation{ + Path: route.Path, + Destination: variable, + } + + return rulesRouteCfg{ + Maps: maps, + Locations: locations, + InternalRedirectLocation: irl, + } +} + +var specialMapParameters = map[string]bool{ + "default": true, + "hostnames": true, + "include": true, + "volatile": true, +} + +func generateValueForRulesRouteMap(matchedValue string) (value string, isNegative bool) { + if len(matchedValue) == 0 { + return `""`, false + } + + if matchedValue[0] == '!' { + isNegative = true + matchedValue = matchedValue[1:] + } + + if _, exists := specialMapParameters[matchedValue]; exists { + return `\` + matchedValue, isNegative + } + + return fmt.Sprintf(`"%s"`, matchedValue), isNegative +} + +func generateParametersForRulesRouteMap(matchedValue string, successfulResult string) []version2.Parameter { + value, isNegative := generateValueForRulesRouteMap(matchedValue) + + valueResult := successfulResult + defaultResult := "0" + if isNegative { + valueResult = "0" + defaultResult = successfulResult + } + + params := []version2.Parameter{ + { + Value: value, + Result: valueResult, + }, + { + Value: "default", + Result: defaultResult, + }, + } + + return params +} + +func getNameForSourceForRulesRouteMapFromCondition(condition conf_v1alpha1.Condition) string { + if condition.Header != "" { + return fmt.Sprintf("$http_%s", strings.ReplaceAll(condition.Header, "-", "_")) + } + + if condition.Cookie != "" { + return fmt.Sprintf("$cookie_%s", condition.Cookie) + } + + if condition.Argument != "" { + return fmt.Sprintf("$arg_%s", condition.Argument) + } + + return condition.Variable +} + +func generateSSLConfig(tls *conf_v1alpha1.TLS, tlsPemFileName string, cfgParams *ConfigParams) *version2.SSL { + if tls == nil { + return nil + } + + if tls.Secret == "" { + return nil + } + + var name string + var ciphers string + + if tlsPemFileName != "" { + name = tlsPemFileName + } else { + name = pemFileNameForMissingTLSSecret + ciphers = "NULL" + } + + ssl := version2.SSL{ + HTTP2: cfgParams.HTTP2, + Certificate: name, + CertificateKey: name, + Ciphers: ciphers, + RedirectToHTTPS: cfgParams.SSLRedirect, + } + + return &ssl +} + +func createUpstreamServersForPlus(virtualServerEx *VirtualServerEx) map[string][]string { + upstreamEndpoints := make(map[string][]string) + + virtualServerUpstreamNamer := newUpstreamNamerForVirtualServer(virtualServerEx.VirtualServer) + + for _, u := range virtualServerEx.VirtualServer.Spec.Upstreams { + endpointsKey := GenerateEndpointsKey(virtualServerEx.VirtualServer.Namespace, u.Service, u.Port) + name := virtualServerUpstreamNamer.GetNameForUpstream(u.Name) + + upstreamEndpoints[name] = virtualServerEx.Endpoints[endpointsKey] + } + + for _, vsr := range virtualServerEx.VirtualServerRoutes { + upstreamNamer := newUpstreamNamerForVirtualServerRoute(virtualServerEx.VirtualServer, vsr) + for _, u := range vsr.Spec.Upstreams { + endpointsKey := GenerateEndpointsKey(vsr.Namespace, u.Service, u.Port) + name := upstreamNamer.GetNameForUpstream(u.Name) + + upstreamEndpoints[name] = virtualServerEx.Endpoints[endpointsKey] + } + } + + return upstreamEndpoints +} + +func createUpstreamServersConfig(baseCfgParams *ConfigParams) nginx.ServerConfig { + cfg := nginx.ServerConfig{ + MaxFails: baseCfgParams.MaxFails, + FailTimeout: baseCfgParams.FailTimeout, + SlowStart: baseCfgParams.SlowStart, + } + + return cfg +} diff --git a/internal/configs/virtualserver_test.go b/internal/configs/virtualserver_test.go new file mode 100644 index 0000000000..3b5f95222d --- /dev/null +++ b/internal/configs/virtualserver_test.go @@ -0,0 +1,1438 @@ +package configs + +import ( + "reflect" + "testing" + + "github.com/nginxinc/kubernetes-ingress/internal/nginx" + + "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" + + conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestVirtualServerExString(t *testing.T) { + tests := []struct { + input *VirtualServerEx + expected string + }{ + { + input: &VirtualServerEx{ + VirtualServer: &conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + }, + }, + expected: "default/cafe", + }, + { + input: &VirtualServerEx{}, + expected: "VirtualServerEx has no VirtualServer", + }, + { + input: nil, + expected: "", + }, + } + + for _, test := range tests { + result := test.input.String() + if result != test.expected { + t.Errorf("VirtualServerEx.String() returned %v but expected %v", result, test.expected) + } + } +} + +func TestGenerateEndpointsKey(t *testing.T) { + serviceNamespace := "default" + serviceName := "test" + var port uint16 = 80 + + expected := "default/test:80" + + result := GenerateEndpointsKey(serviceNamespace, serviceName, port) + if result != expected { + t.Errorf("GenerateEndpointsKey() returned %q but expected %q", result, expected) + } +} + +func TestUpstreamNamerForVirtualServer(t *testing.T) { + virtualServer := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + } + upstreamNamer := newUpstreamNamerForVirtualServer(&virtualServer) + upstream := "test" + + expected := "vs_default_cafe_test" + + result := upstreamNamer.GetNameForUpstream(upstream) + if result != expected { + t.Errorf("GetNameForUpstream() returned %q but expected %q", result, expected) + } +} + +func TestUpstreamNamerForVirtualServerRoute(t *testing.T) { + virtualServer := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + } + virtualServerRoute := conf_v1alpha1.VirtualServerRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee", + Namespace: "default", + }, + } + upstreamNamer := newUpstreamNamerForVirtualServerRoute(&virtualServer, &virtualServerRoute) + upstream := "test" + + expected := "vs_default_cafe_vsr_default_coffee_test" + + result := upstreamNamer.GetNameForUpstream(upstream) + if result != expected { + t.Errorf("GetNameForUpstream() returned %q but expected %q", result, expected) + } +} + +func TestVariableNamerSafeNsName(t *testing.T) { + virtualServer := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe-test", + Namespace: "default", + }, + } + + expected := "default_cafe_test" + + variableNamer := newVariableNamer(&virtualServer) + + if variableNamer.safeNsName != expected { + t.Errorf("newVariableNamer() returned variableNamer with safeNsName=%q but expected %q", variableNamer.safeNsName, expected) + } +} + +func TestVariableNamer(t *testing.T) { + virtualServer := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + } + variableNamer := newVariableNamer(&virtualServer) + + // GetNameForSplitClientVariable() + index := 0 + + expected := "$vs_default_cafe_splits_0" + + result := variableNamer.GetNameForSplitClientVariable(index) + if result != expected { + t.Errorf("GetNameForSplitClientVariable() returned %q but expected %q", result, expected) + } + + // GetNameForVariableForRulesRouteMap() + rulesIndex := 1 + matchIndex := 2 + conditionIndex := 3 + + expected = "$vs_default_cafe_rules_1_match_2_cond_3" + + result = variableNamer.GetNameForVariableForRulesRouteMap(rulesIndex, matchIndex, conditionIndex) + if result != expected { + t.Errorf("GetNameForVariableForRulesRouteMap() returned %q but expected %q", result, expected) + } + + // GetNameForVariableForRulesRouteMainMap() + rulesIndex = 2 + + expected = "$vs_default_cafe_rules_2" + + result = variableNamer.GetNameForVariableForRulesRouteMainMap(rulesIndex) + if result != expected { + t.Errorf("GetNameForVariableForRulesRouteMainMap() returned %q but expected %q", result, expected) + } +} + +func TestGenerateVirtualServerConfig(t *testing.T) { + virtualServerEx := VirtualServerEx{ + VirtualServer: &conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + Host: "cafe.example.com", + Upstreams: []conf_v1alpha1.Upstream{ + { + Name: "tea", + Service: "tea-svc", + Port: 80, + }, + }, + Routes: []conf_v1alpha1.Route{ + { + Path: "/tea", + Upstream: "tea", + }, + { + Path: "/coffee", + Route: "default/coffee", + }, + }, + }, + }, + Endpoints: map[string][]string{ + "default/tea-svc:80": []string{ + "10.0.0.20:80", + }, + "default/coffee-svc:80": []string{ + "10.0.0.30:80", + }, + }, + VirtualServerRoutes: []*conf_v1alpha1.VirtualServerRoute{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee", + Namespace: "default", + }, + Spec: conf_v1alpha1.VirtualServerRouteSpec{ + Host: "cafe.example.com", + Upstreams: []conf_v1alpha1.Upstream{ + { + Name: "coffee", + Service: "coffee-svc", + Port: 80, + }, + }, + Subroutes: []conf_v1alpha1.Route{ + { + Path: "/coffee", + Upstream: "coffee", + }, + }, + }, + }, + }, + } + + baseCfgParams := ConfigParams{ + ServerTokens: "off", + Keepalive: 16, + ServerSnippets: []string{"# server snippet"}, + ProxyProtocol: true, + SetRealIPFrom: []string{"0.0.0.0/0"}, + RealIPHeader: "X-Real-IP", + RealIPRecursive: true, + RedirectToHTTPS: true, + } + + expected := version2.VirtualServerConfig{ + Upstreams: []version2.Upstream{ + { + Name: "vs_default_cafe_tea", + Servers: []version2.UpstreamServer{ + { + Address: "10.0.0.20:80", + }, + }, + }, + { + Name: "vs_default_cafe_vsr_default_coffee_coffee", + Servers: []version2.UpstreamServer{ + { + Address: "10.0.0.30:80", + }, + }, + }, + }, + Server: version2.Server{ + ServerName: "cafe.example.com", + ProxyProtocol: true, + RedirectToHTTPSBasedOnXForwarderProto: true, + ServerTokens: "off", + SetRealIPFrom: []string{"0.0.0.0/0"}, + RealIPHeader: "X-Real-IP", + RealIPRecursive: true, + Snippets: []string{"# server snippet"}, + Locations: []version2.Location{ + { + Path: "/tea", + ProxyPass: "http://vs_default_cafe_tea", + }, + { + Path: "/coffee", + ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee", + }, + }, + }, + Keepalive: "16", + } + + isPlus := false + tlsPemFileName := "" + result := generateVirtualServerConfig(&virtualServerEx, tlsPemFileName, &baseCfgParams, isPlus) + if !reflect.DeepEqual(result, expected) { + t.Errorf("generateVirtualServerConfig returned \n%v but expected \n%v", result, expected) + } +} +func TestGenerateVirtualServerConfigForVirtualServerWithSplits(t *testing.T) { + virtualServerEx := VirtualServerEx{ + VirtualServer: &conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + Host: "cafe.example.com", + Upstreams: []conf_v1alpha1.Upstream{ + { + Name: "tea-v1", + Service: "tea-svc-v1", + Port: 80, + }, + { + Name: "tea-v2", + Service: "tea-svc-v2", + Port: 80, + }, + }, + Routes: []conf_v1alpha1.Route{ + { + Path: "/tea", + Splits: []conf_v1alpha1.Split{ + { + Weight: 90, + Upstream: "tea-v1", + }, + { + Weight: 10, + Upstream: "tea-v2", + }, + }, + }, + { + Path: "/coffee", + Route: "default/coffee", + }, + }, + }, + }, + Endpoints: map[string][]string{ + "default/tea-svc-v1:80": []string{ + "10.0.0.20:80", + }, + "default/tea-svc-v2:80": []string{ + "10.0.0.21:80", + }, + "default/coffee-svc-v1:80": []string{ + "10.0.0.30:80", + }, + "default/coffee-svc-v2:80": []string{ + "10.0.0.31:80", + }, + }, + VirtualServerRoutes: []*conf_v1alpha1.VirtualServerRoute{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee", + Namespace: "default", + }, + Spec: conf_v1alpha1.VirtualServerRouteSpec{ + Host: "cafe.example.com", + Upstreams: []conf_v1alpha1.Upstream{ + { + Name: "coffee-v1", + Service: "coffee-svc-v1", + Port: 80, + }, + { + Name: "coffee-v2", + Service: "coffee-svc-v2", + Port: 80, + }, + }, + Subroutes: []conf_v1alpha1.Route{ + { + Path: "/coffee", + Splits: []conf_v1alpha1.Split{ + { + Weight: 40, + Upstream: "coffee-v1", + }, + { + Weight: 60, + Upstream: "coffee-v2", + }, + }, + }, + }, + }, + }, + }, + } + + baseCfgParams := ConfigParams{} + + expected := version2.VirtualServerConfig{ + Upstreams: []version2.Upstream{ + { + Name: "vs_default_cafe_tea-v1", + Servers: []version2.UpstreamServer{ + { + Address: "10.0.0.20:80", + }, + }, + }, + { + Name: "vs_default_cafe_tea-v2", + Servers: []version2.UpstreamServer{ + { + Address: "10.0.0.21:80", + }, + }, + }, + { + Name: "vs_default_cafe_vsr_default_coffee_coffee-v1", + Servers: []version2.UpstreamServer{ + { + Address: "10.0.0.30:80", + }, + }, + }, + { + Name: "vs_default_cafe_vsr_default_coffee_coffee-v2", + Servers: []version2.UpstreamServer{ + { + Address: "10.0.0.31:80", + }, + }, + }, + }, + SplitClients: []version2.SplitClient{ + { + Source: "$request_id", + Variable: "$vs_default_cafe_splits_0", + Distributions: []version2.Distribution{ + { + Weight: "90%", + Value: "@splits_0_split_0", + }, + { + Weight: "10%", + Value: "@splits_0_split_1", + }, + }, + }, + { + Source: "$request_id", + Variable: "$vs_default_cafe_splits_1", + Distributions: []version2.Distribution{ + { + Weight: "40%", + Value: "@splits_1_split_0", + }, + { + Weight: "60%", + Value: "@splits_1_split_1", + }, + }, + }, + }, + Server: version2.Server{ + ServerName: "cafe.example.com", + InternalRedirectLocations: []version2.InternalRedirectLocation{ + { + Path: "/tea", + Destination: "$vs_default_cafe_splits_0", + }, + { + Path: "/coffee", + Destination: "$vs_default_cafe_splits_1", + }, + }, + Locations: []version2.Location{ + { + Path: "@splits_0_split_0", + ProxyPass: "http://vs_default_cafe_tea-v1", + }, + { + Path: "@splits_0_split_1", + ProxyPass: "http://vs_default_cafe_tea-v2", + }, + { + Path: "@splits_1_split_0", + ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v1", + }, + { + Path: "@splits_1_split_1", + ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v2", + }, + }, + }, + } + + isPlus := false + tlsPemFileName := "" + result := generateVirtualServerConfig(&virtualServerEx, tlsPemFileName, &baseCfgParams, isPlus) + if !reflect.DeepEqual(result, expected) { + t.Errorf("generateVirtualServerConfig returned \n%v but expected \n%v", result, expected) + } +} + +func TestGenerateVirtualServerConfigForVirtualServerWithRules(t *testing.T) { + virtualServerEx := VirtualServerEx{ + VirtualServer: &conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + Host: "cafe.example.com", + Upstreams: []conf_v1alpha1.Upstream{ + { + Name: "tea-v1", + Service: "tea-svc-v1", + Port: 80, + }, + { + Name: "tea-v2", + Service: "tea-svc-v2", + Port: 80, + }, + }, + Routes: []conf_v1alpha1.Route{ + { + Path: "/tea", + Rules: &conf_v1alpha1.Rules{ + Conditions: []conf_v1alpha1.Condition{ + { + Header: "x-version", + }, + }, + Matches: []conf_v1alpha1.Match{ + { + Values: []string{ + "v2", + }, + Upstream: "tea-v2", + }, + }, + DefaultUpstream: "tea-v1", + }, + }, + { + Path: "/coffee", + Route: "default/coffee", + }, + }, + }, + }, + Endpoints: map[string][]string{ + "default/tea-svc-v1:80": []string{ + "10.0.0.20:80", + }, + "default/tea-svc-v2:80": []string{ + "10.0.0.21:80", + }, + "default/coffee-svc-v1:80": []string{ + "10.0.0.30:80", + }, + "default/coffee-svc-v2:80": []string{ + "10.0.0.31:80", + }, + }, + VirtualServerRoutes: []*conf_v1alpha1.VirtualServerRoute{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee", + Namespace: "default", + }, + Spec: conf_v1alpha1.VirtualServerRouteSpec{ + Host: "cafe.example.com", + Upstreams: []conf_v1alpha1.Upstream{ + { + Name: "coffee-v1", + Service: "coffee-svc-v1", + Port: 80, + }, + { + Name: "coffee-v2", + Service: "coffee-svc-v2", + Port: 80, + }, + }, + Subroutes: []conf_v1alpha1.Route{ + { + Path: "/coffee", + Rules: &conf_v1alpha1.Rules{ + Conditions: []conf_v1alpha1.Condition{ + { + Argument: "version", + }, + }, + Matches: []conf_v1alpha1.Match{ + { + Values: []string{ + "v2", + }, + Upstream: "coffee-v2", + }, + }, + DefaultUpstream: "coffee-v1", + }, + }, + }, + }, + }, + }, + } + + baseCfgParams := ConfigParams{} + + expected := version2.VirtualServerConfig{ + Upstreams: []version2.Upstream{ + { + Name: "vs_default_cafe_tea-v1", + Servers: []version2.UpstreamServer{ + { + Address: "10.0.0.20:80", + }, + }, + }, + { + Name: "vs_default_cafe_tea-v2", + Servers: []version2.UpstreamServer{ + { + Address: "10.0.0.21:80", + }, + }, + }, + { + Name: "vs_default_cafe_vsr_default_coffee_coffee-v1", + Servers: []version2.UpstreamServer{ + { + Address: "10.0.0.30:80", + }, + }, + }, + { + Name: "vs_default_cafe_vsr_default_coffee_coffee-v2", + Servers: []version2.UpstreamServer{ + { + Address: "10.0.0.31:80", + }, + }, + }, + }, + Maps: []version2.Map{ + { + Source: "$http_x_version", + Variable: "$vs_default_cafe_rules_0_match_0_cond_0", + Parameters: []version2.Parameter{ + { + Value: `"v2"`, + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + Source: "$vs_default_cafe_rules_0_match_0_cond_0", + Variable: "$vs_default_cafe_rules_0", + Parameters: []version2.Parameter{ + { + Value: "~^1", + Result: "@rules_0_match_0", + }, + { + Value: "default", + Result: "@rules_0_default", + }, + }, + }, + { + Source: "$arg_version", + Variable: "$vs_default_cafe_rules_1_match_0_cond_0", + Parameters: []version2.Parameter{ + { + Value: `"v2"`, + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + Source: "$vs_default_cafe_rules_1_match_0_cond_0", + Variable: "$vs_default_cafe_rules_1", + Parameters: []version2.Parameter{ + { + Value: "~^1", + Result: "@rules_1_match_0", + }, + { + Value: "default", + Result: "@rules_1_default", + }, + }, + }, + }, + Server: version2.Server{ + ServerName: "cafe.example.com", + InternalRedirectLocations: []version2.InternalRedirectLocation{ + { + Path: "/tea", + Destination: "$vs_default_cafe_rules_0", + }, + { + Path: "/coffee", + Destination: "$vs_default_cafe_rules_1", + }, + }, + Locations: []version2.Location{ + { + Path: "@rules_0_match_0", + ProxyPass: "http://vs_default_cafe_tea-v2", + }, + { + Path: "@rules_0_default", + ProxyPass: "http://vs_default_cafe_tea-v1", + }, + { + Path: "@rules_1_match_0", + ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v2", + }, + { + Path: "@rules_1_default", + ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v1", + }, + }, + }, + } + + isPlus := false + tlsPemFileName := "" + result := generateVirtualServerConfig(&virtualServerEx, tlsPemFileName, &baseCfgParams, isPlus) + if !reflect.DeepEqual(result, expected) { + t.Errorf("generateVirtualServerConfig returned \n%v but expected \n%v", result, expected) + } +} + +func TestGenerateUpstream(t *testing.T) { + name := "test-upstream" + endpoints := []string{ + "192.168.10.10:8080", + } + isPlus := false + cfgParams := ConfigParams{ + LBMethod: "random", + MaxFails: 1, + FailTimeout: "10s", + } + + expected := version2.Upstream{ + Name: "test-upstream", + Servers: []version2.UpstreamServer{ + { + Address: "192.168.10.10:8080", + MaxFails: 1, + FailTimeout: "10s", + }, + }, + LBMethod: "random", + } + + result := generateUpstream(name, endpoints, isPlus, &cfgParams) + if !reflect.DeepEqual(result, expected) { + t.Errorf("generateUpstream() returned %v but expected %v", result, expected) + } +} + +func TestGenerateUpstreamForZeroEndpoints(t *testing.T) { + name := "test-upstream" + var endpoints []string // nil + cfgParams := ConfigParams{ + MaxFails: 1, + FailTimeout: "10s", + } + + isPlus := false + expectedForNGINX := version2.Upstream{ + Name: "test-upstream", + Servers: []version2.UpstreamServer{ + { + Address: nginx502Server, + MaxFails: 1, + FailTimeout: "10s", + }, + }, + } + + result := generateUpstream(name, endpoints, isPlus, &cfgParams) + if !reflect.DeepEqual(result, expectedForNGINX) { + t.Errorf("generateUpstream(isPlus=%v) returned %v but expected %v", isPlus, result, expectedForNGINX) + } + + isPlus = true + expectedForNGINXPlus := version2.Upstream{ + Name: "test-upstream", + Servers: nil, + } + + result = generateUpstream(name, endpoints, isPlus, &cfgParams) + if !reflect.DeepEqual(result, expectedForNGINXPlus) { + t.Errorf("generateUpstream(isPlus=%v) returned %v but expected %v", isPlus, result, expectedForNGINXPlus) + } +} + +func TestGenerateLocation(t *testing.T) { + cfgParams := ConfigParams{ + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ClientMaxBodySize: "1m", + ProxyMaxTempFileSize: "1024m", + ProxyBuffering: true, + ProxyBuffers: "8 4k", + ProxyBufferSize: "4k", + LocationSnippets: []string{"# location snippet"}, + } + path := "/" + upstreamName := "test-upstream" + + expected := version2.Location{ + Path: "/", + Snippets: []string{"# location snippet"}, + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ClientMaxBodySize: "1m", + ProxyMaxTempFileSize: "1024m", + ProxyBuffering: true, + ProxyBuffers: "8 4k", + ProxyBufferSize: "4k", + ProxyPass: "http://test-upstream", + } + + result := generateLocation(path, upstreamName, &cfgParams) + if !reflect.DeepEqual(result, expected) { + t.Errorf("generateLocation() returned %v but expected %v", result, expected) + } +} + +func TestGenerateSSLConfig(t *testing.T) { + tests := []struct { + inputTLS *conf_v1alpha1.TLS + inputTLSPemFileName string + inputCfgParams *ConfigParams + expected *version2.SSL + msg string + }{ + { + inputTLS: nil, + inputTLSPemFileName: "", + inputCfgParams: &ConfigParams{}, + expected: nil, + msg: "no TLS field", + }, + { + inputTLS: &conf_v1alpha1.TLS{ + Secret: "", + }, + inputTLSPemFileName: "", + inputCfgParams: &ConfigParams{}, + expected: nil, + msg: "TLS field with empty secret", + }, + { + inputTLS: &conf_v1alpha1.TLS{ + Secret: "secret", + }, + inputTLSPemFileName: "", + inputCfgParams: &ConfigParams{}, + expected: &version2.SSL{ + HTTP2: false, + Certificate: pemFileNameForMissingTLSSecret, + CertificateKey: pemFileNameForMissingTLSSecret, + Ciphers: "NULL", + RedirectToHTTPS: false, + }, + msg: "secret doesn't exist in the cluster with HTTP2 and SSLRedirect disabled", + }, + { + inputTLS: &conf_v1alpha1.TLS{ + Secret: "secret", + }, + inputTLSPemFileName: "secret.pem", + inputCfgParams: &ConfigParams{}, + expected: &version2.SSL{ + HTTP2: false, + Certificate: "secret.pem", + CertificateKey: "secret.pem", + Ciphers: "", + RedirectToHTTPS: false, + }, + msg: "normal case with HTTP2 and SSLRedirect disabled", + }, + { + inputTLS: &conf_v1alpha1.TLS{ + Secret: "secret", + }, + inputTLSPemFileName: "secret.pem", + inputCfgParams: &ConfigParams{ + HTTP2: true, + SSLRedirect: true, + }, + expected: &version2.SSL{ + HTTP2: true, + Certificate: "secret.pem", + CertificateKey: "secret.pem", + Ciphers: "", + RedirectToHTTPS: true, + }, + msg: "normal case with HTTP2 and SSLRedirect enabled", + }, + } + + for _, test := range tests { + result := generateSSLConfig(test.inputTLS, test.inputTLSPemFileName, test.inputCfgParams) + if !reflect.DeepEqual(result, test.expected) { + t.Errorf("generateSSLConfig() returned %v but expected %v for the case of %s", result, test.expected, test.msg) + } + } +} + +func TestCreateUpstreamServersForPlus(t *testing.T) { + virtualServerEx := VirtualServerEx{ + VirtualServer: &conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + Host: "cafe.example.com", + Upstreams: []conf_v1alpha1.Upstream{ + { + Name: "tea", + Service: "tea-svc", + Port: 80, + }, + { + Name: "test", + Service: "test-svc", + Port: 80, + }, + }, + Routes: []conf_v1alpha1.Route{ + { + Path: "/tea", + Upstream: "tea", + }, + { + Path: "/coffee", + Route: "default/coffee", + }, + }, + }, + }, + Endpoints: map[string][]string{ + "default/tea-svc:80": []string{ + "10.0.0.20:80", + }, + "default/test-svc:80": []string{}, + "default/coffee-svc:80": []string{ + "10.0.0.30:80", + }, + }, + VirtualServerRoutes: []*conf_v1alpha1.VirtualServerRoute{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee", + Namespace: "default", + }, + Spec: conf_v1alpha1.VirtualServerRouteSpec{ + Host: "cafe.example.com", + Upstreams: []conf_v1alpha1.Upstream{ + { + Name: "coffee", + Service: "coffee-svc", + Port: 80, + }, + }, + Subroutes: []conf_v1alpha1.Route{ + { + Path: "/coffee", + Upstream: "coffee", + }, + }, + }, + }, + }, + } + + expected := map[string][]string{ + "vs_default_cafe_tea": []string{ + "10.0.0.20:80", + }, + "vs_default_cafe_test": []string{}, + "vs_default_cafe_vsr_default_coffee_coffee": []string{ + "10.0.0.30:80", + }, + } + + result := createUpstreamServersForPlus(&virtualServerEx) + if !reflect.DeepEqual(result, expected) { + t.Errorf("createUpstreamServersForPlus returned %v but expected %v", result, expected) + } +} + +func TestCreateUpstreamServersConfig(t *testing.T) { + baseCfgParams := ConfigParams{ + MaxFails: 5, + FailTimeout: "30s", + SlowStart: "50s", + } + + expected := nginx.ServerConfig{ + MaxFails: 5, + FailTimeout: "30s", + SlowStart: "50s", + } + + result := createUpstreamServersConfig(&baseCfgParams) + if !reflect.DeepEqual(result, expected) { + t.Errorf("createUpstreamServersConfig returned %v but expected %v", result, expected) + } +} + +func TestGenerateSplitRouteConfig(t *testing.T) { + route := conf_v1alpha1.Route{ + Path: "/", + Splits: []conf_v1alpha1.Split{ + { + Weight: 90, + Upstream: "coffee-v1", + }, + { + Weight: 10, + Upstream: "coffee-v2", + }, + }, + } + virtualServer := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + } + upstreamNamer := newUpstreamNamerForVirtualServer(&virtualServer) + variableNamer := newVariableNamer(&virtualServer) + index := 1 + + expected := splitRouteCfg{ + SplitClient: version2.SplitClient{ + Source: "$request_id", + Variable: "$vs_default_cafe_splits_1", + Distributions: []version2.Distribution{ + { + Weight: "90%", + Value: "@splits_1_split_0", + }, + { + Weight: "10%", + Value: "@splits_1_split_1", + }, + }, + }, + Locations: []version2.Location{ + { + Path: "@splits_1_split_0", + ProxyPass: "http://vs_default_cafe_coffee-v1", + }, + { + Path: "@splits_1_split_1", + ProxyPass: "http://vs_default_cafe_coffee-v2", + }, + }, + InternalRedirectLocation: version2.InternalRedirectLocation{ + Path: "/", + Destination: "$vs_default_cafe_splits_1", + }, + } + + cfgParams := ConfigParams{} + + result := generateSplitRouteConfig(route, upstreamNamer, variableNamer, index, &cfgParams) + if !reflect.DeepEqual(result, expected) { + t.Errorf("generateSplitRouteConfig() returned %v but expected %v", result, expected) + } +} + +func TestGenerateRulesRouteConfig(t *testing.T) { + route := conf_v1alpha1.Route{ + Path: "/", + Rules: &conf_v1alpha1.Rules{ + Conditions: []conf_v1alpha1.Condition{ + { + Header: "x-version", + }, + { + Cookie: "user", + }, + { + Argument: "answer", + }, + { + Variable: "$request_method", + }, + }, + Matches: []conf_v1alpha1.Match{ + { + Values: []string{ + "v1", + "john", + "yes", + "GET", + }, + Upstream: "coffee-v1", + }, + { + Values: []string{ + "v2", + "paul", + "no", + "POST", + }, + Upstream: "coffee-v2", + }, + }, + DefaultUpstream: "tea", + }, + } + virtualServer := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + } + upstreamNamer := newUpstreamNamerForVirtualServer(&virtualServer) + variableNamer := newVariableNamer(&virtualServer) + index := 1 + + expected := rulesRouteCfg{ + Maps: []version2.Map{ + { + Source: "$http_x_version", + Variable: "$vs_default_cafe_rules_1_match_0_cond_0", + Parameters: []version2.Parameter{ + { + Value: `"v1"`, + Result: "$vs_default_cafe_rules_1_match_0_cond_1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + Source: "$cookie_user", + Variable: "$vs_default_cafe_rules_1_match_0_cond_1", + Parameters: []version2.Parameter{ + { + Value: `"john"`, + Result: "$vs_default_cafe_rules_1_match_0_cond_2", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + Source: "$arg_answer", + Variable: "$vs_default_cafe_rules_1_match_0_cond_2", + Parameters: []version2.Parameter{ + { + Value: `"yes"`, + Result: "$vs_default_cafe_rules_1_match_0_cond_3", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + Source: "$request_method", + Variable: "$vs_default_cafe_rules_1_match_0_cond_3", + Parameters: []version2.Parameter{ + { + Value: `"GET"`, + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + Source: "$http_x_version", + Variable: "$vs_default_cafe_rules_1_match_1_cond_0", + Parameters: []version2.Parameter{ + { + Value: `"v2"`, + Result: "$vs_default_cafe_rules_1_match_1_cond_1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + Source: "$cookie_user", + Variable: "$vs_default_cafe_rules_1_match_1_cond_1", + Parameters: []version2.Parameter{ + { + Value: `"paul"`, + Result: "$vs_default_cafe_rules_1_match_1_cond_2", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + Source: "$arg_answer", + Variable: "$vs_default_cafe_rules_1_match_1_cond_2", + Parameters: []version2.Parameter{ + { + Value: `"no"`, + Result: "$vs_default_cafe_rules_1_match_1_cond_3", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + Source: "$request_method", + Variable: "$vs_default_cafe_rules_1_match_1_cond_3", + Parameters: []version2.Parameter{ + { + Value: `"POST"`, + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + Source: "$vs_default_cafe_rules_1_match_0_cond_0$vs_default_cafe_rules_1_match_1_cond_0", + Variable: "$vs_default_cafe_rules_1", + Parameters: []version2.Parameter{ + { + Value: "~^1", + Result: "@rules_1_match_0", + }, + { + Value: "~^01", + Result: "@rules_1_match_1", + }, + { + Value: "default", + Result: "@rules_1_default", + }, + }, + }, + }, + Locations: []version2.Location{ + { + Path: "@rules_1_match_0", + ProxyPass: "http://vs_default_cafe_coffee-v1", + }, + { + Path: "@rules_1_match_1", + ProxyPass: "http://vs_default_cafe_coffee-v2", + }, + { + Path: "@rules_1_default", + ProxyPass: "http://vs_default_cafe_tea", + }, + }, + InternalRedirectLocation: version2.InternalRedirectLocation{ + Path: "/", + Destination: "$vs_default_cafe_rules_1", + }, + } + + cfgParams := ConfigParams{} + + result := generateRulesRouteConfig(route, upstreamNamer, variableNamer, index, &cfgParams) + if !reflect.DeepEqual(result, expected) { + t.Errorf("generateRulesRouteConfig() returned \n%v but expected \n%v", result, expected) + } +} + +func TestGenerateValueForRulesRouteMap(t *testing.T) { + tests := []struct { + input string + expectedValue string + expectedIsNegative bool + }{ + { + input: "default", + expectedValue: `\default`, + expectedIsNegative: false, + }, + { + input: "!default", + expectedValue: `\default`, + expectedIsNegative: true, + }, + { + input: "hostnames", + expectedValue: `\hostnames`, + expectedIsNegative: false, + }, + { + input: "include", + expectedValue: `\include`, + expectedIsNegative: false, + }, + { + input: "volatile", + expectedValue: `\volatile`, + expectedIsNegative: false, + }, + { + input: "abc", + expectedValue: `"abc"`, + expectedIsNegative: false, + }, + { + input: "!abc", + expectedValue: `"abc"`, + expectedIsNegative: true, + }, + { + input: "", + expectedValue: `""`, + expectedIsNegative: false, + }, + { + input: "!", + expectedValue: `""`, + expectedIsNegative: true, + }, + } + + for _, test := range tests { + resultValue, resultIsNegative := generateValueForRulesRouteMap(test.input) + if resultValue != test.expectedValue { + t.Errorf("generateValueForRulesRouteMap(%q) returned %q but expected %q as the value", test.input, resultValue, test.expectedValue) + } + if resultIsNegative != test.expectedIsNegative { + t.Errorf("generateValueForRulesRouteMap(%q) returned %v but expected %v as the isNegative", test.input, resultIsNegative, test.expectedIsNegative) + } + } +} + +func TestGenerateParametersForRulesRouteMap(t *testing.T) { + tests := []struct { + inputMatchedValue string + inputSuccessfulResult string + expected []version2.Parameter + }{ + { + inputMatchedValue: "abc", + inputSuccessfulResult: "1", + expected: []version2.Parameter{ + { + Value: `"abc"`, + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + { + inputMatchedValue: "!abc", + inputSuccessfulResult: "1", + expected: []version2.Parameter{ + { + Value: `"abc"`, + Result: "0", + }, + { + Value: "default", + Result: "1", + }, + }, + }, + } + + for _, test := range tests { + result := generateParametersForRulesRouteMap(test.inputMatchedValue, test.inputSuccessfulResult) + if !reflect.DeepEqual(result, test.expected) { + t.Errorf("generateParametersForRulesRouteMap(%q, %q) returned %v but expected %v", test.inputMatchedValue, test.inputSuccessfulResult, result, test.expected) + } + } +} + +func TestGetNameForSourceForRulesRouteMapFromCondition(t *testing.T) { + tests := []struct { + input conf_v1alpha1.Condition + expected string + }{ + { + input: conf_v1alpha1.Condition{ + Header: "x-version", + }, + expected: "$http_x_version", + }, + { + input: conf_v1alpha1.Condition{ + Cookie: "mycookie", + }, + expected: "$cookie_mycookie", + }, + { + input: conf_v1alpha1.Condition{ + Argument: "arg", + }, + expected: "$arg_arg", + }, + { + input: conf_v1alpha1.Condition{ + Variable: "$request_method", + }, + expected: "$request_method", + }, + } + + for _, test := range tests { + result := getNameForSourceForRulesRouteMapFromCondition(test.input) + if result != test.expected { + t.Errorf("getNameForSourceForRulesRouteMapFromCondition() returned %q but expected %q for input %v", result, test.expected, test.input) + } + } +} diff --git a/internal/k8s/controller.go b/internal/k8s/controller.go index 779fa3e609..0707ef9ed2 100644 --- a/internal/k8s/controller.go +++ b/internal/k8s/controller.go @@ -18,7 +18,9 @@ package k8s import ( "context" + "errors" "fmt" + "strings" "time" "github.com/golang/glog" @@ -38,6 +40,9 @@ import ( "sort" + conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/validation" + k8s_nginx "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned" api_v1 "k8s.io/api/core/v1" extensions "k8s.io/api/extensions/v1beta1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -50,78 +55,88 @@ const ( // LoadBalancerController watches Kubernetes API and // reconfigures NGINX via NginxController when needed type LoadBalancerController struct { - client kubernetes.Interface - ingressController cache.Controller - svcController cache.Controller - endpointController cache.Controller - configMapController cache.Controller - secretController cache.Controller - ingressLister storeToIngressLister - svcLister cache.Store - endpointLister storeToEndpointLister - configMapLister storeToConfigMapLister - secretLister storeToSecretLister - syncQueue *taskQueue - ctx context.Context - cancel context.CancelFunc - configurator *configs.Configurator - watchNginxConfigMaps bool - isNginxPlus bool - recorder record.EventRecorder - defaultServerSecret string - ingressClass string - useIngressClassOnly bool - statusUpdater *statusUpdater - leaderElector *leaderelection.LeaderElector - reportIngressStatus bool - isLeaderElectionEnabled bool - leaderElectionLockName string - resync time.Duration - namespace string - controllerNamespace string - wildcardTLSSecret string - metricsCollector collectors.ControllerCollector + client kubernetes.Interface + confClient k8s_nginx.Interface + ingressController cache.Controller + svcController cache.Controller + endpointController cache.Controller + configMapController cache.Controller + secretController cache.Controller + virtualServerController cache.Controller + virtualServerRouteController cache.Controller + ingressLister storeToIngressLister + svcLister cache.Store + endpointLister storeToEndpointLister + configMapLister storeToConfigMapLister + secretLister storeToSecretLister + virtualServerLister cache.Store + virtualServerRouteLister cache.Store + syncQueue *taskQueue + ctx context.Context + cancel context.CancelFunc + configurator *configs.Configurator + watchNginxConfigMaps bool + isNginxPlus bool + recorder record.EventRecorder + defaultServerSecret string + ingressClass string + useIngressClassOnly bool + statusUpdater *statusUpdater + leaderElector *leaderelection.LeaderElector + reportIngressStatus bool + isLeaderElectionEnabled bool + leaderElectionLockName string + resync time.Duration + namespace string + controllerNamespace string + wildcardTLSSecret string + areCustomResourcesEnabled bool + metricsCollector collectors.ControllerCollector } var keyFunc = cache.DeletionHandlingMetaNamespaceKeyFunc // NewLoadBalancerControllerInput holds the input needed to call NewLoadBalancerController. type NewLoadBalancerControllerInput struct { - KubeClient kubernetes.Interface - ResyncPeriod time.Duration - Namespace string - NginxConfigurator *configs.Configurator - DefaultServerSecret string - IsNginxPlus bool - IngressClass string - UseIngressClassOnly bool - ExternalServiceName string - ControllerNamespace string - ReportIngressStatus bool - IsLeaderElectionEnabled bool - LeaderElectionLockName string - WildcardTLSSecret string - ConfigMaps string - MetricsCollector collectors.ControllerCollector + KubeClient kubernetes.Interface + ConfClient k8s_nginx.Interface + ResyncPeriod time.Duration + Namespace string + NginxConfigurator *configs.Configurator + DefaultServerSecret string + IsNginxPlus bool + IngressClass string + UseIngressClassOnly bool + ExternalServiceName string + ControllerNamespace string + ReportIngressStatus bool + IsLeaderElectionEnabled bool + LeaderElectionLockName string + WildcardTLSSecret string + ConfigMaps string + AreCustomResourcesEnabled bool + MetricsCollector collectors.ControllerCollector } // NewLoadBalancerController creates a controller func NewLoadBalancerController(input NewLoadBalancerControllerInput) *LoadBalancerController { lbc := &LoadBalancerController{ - client: input.KubeClient, - configurator: input.NginxConfigurator, - defaultServerSecret: input.DefaultServerSecret, - isNginxPlus: input.IsNginxPlus, - ingressClass: input.IngressClass, - useIngressClassOnly: input.UseIngressClassOnly, - reportIngressStatus: input.ReportIngressStatus, - isLeaderElectionEnabled: input.IsLeaderElectionEnabled, - leaderElectionLockName: input.LeaderElectionLockName, - resync: input.ResyncPeriod, - namespace: input.Namespace, - controllerNamespace: input.ControllerNamespace, - wildcardTLSSecret: input.WildcardTLSSecret, - metricsCollector: input.MetricsCollector, + client: input.KubeClient, + confClient: input.ConfClient, + configurator: input.NginxConfigurator, + defaultServerSecret: input.DefaultServerSecret, + isNginxPlus: input.IsNginxPlus, + ingressClass: input.IngressClass, + useIngressClassOnly: input.UseIngressClassOnly, + reportIngressStatus: input.ReportIngressStatus, + isLeaderElectionEnabled: input.IsLeaderElectionEnabled, + leaderElectionLockName: input.LeaderElectionLockName, + resync: input.ResyncPeriod, + namespace: input.Namespace, + controllerNamespace: input.ControllerNamespace, + wildcardTLSSecret: input.WildcardTLSSecret, + areCustomResourcesEnabled: input.AreCustomResourcesEnabled, + metricsCollector: input.MetricsCollector, } eventBroadcaster := record.NewBroadcaster() @@ -150,6 +165,11 @@ func NewLoadBalancerController(input NewLoadBalancerControllerInput) *LoadBalanc lbc.addServiceHandler(createServiceHandlers(lbc)) lbc.addEndpointHandler(createEndpointHandlers(lbc)) + if lbc.areCustomResourcesEnabled { + lbc.addVirtualServerHandler(createVirtualServerHandlers(lbc)) + lbc.addVirtualServerRouteHandler(createVirtualServerRouteHandlers(lbc)) + } + if input.ConfigMaps != "" { nginxConfigMapsNS, nginxConfigMapsName, err := ParseNamespaceName(input.ConfigMaps) if err != nil { @@ -256,6 +276,32 @@ func (lbc *LoadBalancerController) addConfigMapHandler(handlers cache.ResourceEv ) } +func (lbc *LoadBalancerController) addVirtualServerHandler(handlers cache.ResourceEventHandlerFuncs) { + lbc.virtualServerLister, lbc.virtualServerController = cache.NewInformer( + cache.NewListWatchFromClient( + lbc.confClient.K8sV1alpha1().RESTClient(), + "virtualservers", + lbc.namespace, + fields.Everything()), + &conf_v1alpha1.VirtualServer{}, + lbc.resync, + handlers, + ) +} + +func (lbc *LoadBalancerController) addVirtualServerRouteHandler(handlers cache.ResourceEventHandlerFuncs) { + lbc.virtualServerRouteLister, lbc.virtualServerRouteController = cache.NewInformer( + cache.NewListWatchFromClient( + lbc.confClient.K8sV1alpha1().RESTClient(), + "virtualserverroutes", + lbc.namespace, + fields.Everything()), + &conf_v1alpha1.VirtualServerRoute{}, + lbc.resync, + handlers, + ) +} + // Run starts the loadbalancer controller func (lbc *LoadBalancerController) Run() { lbc.ctx, lbc.cancel = context.WithCancel(context.Background()) @@ -270,6 +316,10 @@ func (lbc *LoadBalancerController) Run() { go lbc.configMapController.Run(lbc.ctx.Done()) } go lbc.ingressController.Run(lbc.ctx.Done()) + if lbc.areCustomResourcesEnabled { + go lbc.virtualServerController.Run(lbc.ctx.Done()) + go lbc.virtualServerRouteController.Run(lbc.ctx.Done()) + } go lbc.syncQueue.Run(time.Second, lbc.ctx.Done()) <-lbc.ctx.Done() } @@ -345,6 +395,19 @@ func (lbc *LoadBalancerController) syncEndpoint(task task) { glog.Errorf("Error updating endpoints for %v: %v", mergableIngressesSlice, err) } } + + if lbc.areCustomResourcesEnabled { + virtualServers := lbc.getVirtualServersForEndpoints(obj.(*api_v1.Endpoints)) + virtualServersExes := lbc.virtualServersToVirtualServerExes(virtualServers) + + if len(virtualServersExes) > 0 { + glog.V(3).Infof("Updating endpoints for %v", virtualServersExes) + err = lbc.configurator.UpdateEndpointsForVirtualServers(virtualServersExes) + if err != nil { + glog.Errorf("Error updating endpoints for %v: %v", virtualServersExes, err) + } + } + } } } @@ -376,7 +439,13 @@ func (lbc *LoadBalancerController) syncConfig(task task) { } } - updateErr := lbc.configurator.UpdateConfig(cfgParams, ingExes, mergeableIngresses) + var virtualServerExes []*configs.VirtualServerEx + if lbc.areCustomResourcesEnabled { + virtualServers := lbc.getVirtualServers() + virtualServerExes = lbc.virtualServersToVirtualServerExes(virtualServers) + } + + updateErr := lbc.configurator.UpdateConfig(cfgParams, ingExes, mergeableIngresses, virtualServerExes) eventTitle := "Updated" eventType := api_v1.EventTypeNormal @@ -403,6 +472,14 @@ func (lbc *LoadBalancerController) syncConfig(task task) { minion.Ingress.Namespace, minion.Ingress.Name, eventWarningMessage) } } + for _, vsEx := range virtualServerExes { + lbc.recorder.Eventf(vsEx.VirtualServer, eventType, eventTitle, "Configuration for %v/%v was updated %s", + vsEx.VirtualServer.Namespace, vsEx.VirtualServer.Name, eventWarningMessage) + for _, vsr := range vsEx.VirtualServerRoutes { + lbc.recorder.Eventf(vsr, eventType, eventTitle, "Configuration for %v/%v was updated %s", + vsr.Namespace, vsr.Name, eventWarningMessage) + } + } } // GetManagedIngresses gets Ingress resources that the IC is currently responsible for @@ -454,6 +531,17 @@ func (lbc *LoadBalancerController) ingressesToIngressExes(ings []extensions.Ingr return ingExes } +func (lbc *LoadBalancerController) virtualServersToVirtualServerExes(virtualServers []*conf_v1alpha1.VirtualServer) []*configs.VirtualServerEx { + var virtualServersExes []*configs.VirtualServerEx + + for _, vs := range virtualServers { + vsEx, _ := lbc.createVirtualServer(vs) // ignoring VirtualServerRouteErrors + virtualServersExes = append(virtualServersExes, vsEx) + } + + return virtualServersExes +} + func (lbc *LoadBalancerController) sync(task task) { glog.V(3).Infof("Syncing %v", task.Key) @@ -472,6 +560,103 @@ func (lbc *LoadBalancerController) sync(task task) { lbc.syncSecret(task) case service: lbc.syncExternalService(task) + case virtualserver: + lbc.syncVirtualServer(task) + case virtualServerRoute: + lbc.syncVirtualServerRoute(task) + } +} + +func (lbc *LoadBalancerController) syncVirtualServer(task task) { + key := task.Key + obj, vsExists, err := lbc.virtualServerLister.GetByKey(key) + if err != nil { + lbc.syncQueue.Requeue(task, err) + return + } + + if !vsExists { + glog.V(2).Infof("Deleting VirtualServer: %v\n", key) + + err := lbc.configurator.DeleteVirtualServer(key) + // TO-DO: emit events for referenced VirtualServerRoutes + if err != nil { + glog.Errorf("Error when deleting configuration for %v: %v", key, err) + } + return + } + + glog.V(2).Infof("Adding or Updating VirtualServer: %v\n", key) + + vs := obj.(*conf_v1alpha1.VirtualServer) + + validationErr := validation.ValidateVirtualServer(vs) + if validationErr != nil { + err := lbc.configurator.DeleteVirtualServer(key) + if err != nil { + glog.Errorf("Error when deleting configuration for %v: %v", key, err) + } + lbc.recorder.Eventf(vs, api_v1.EventTypeWarning, "Rejected", "VirtualServer %v is invalid and was rejected: %v", key, validationErr) + // TO-DO: emit events for referenced VirtualServerRoutes + return + } + + vsEx, vsrErrors := lbc.createVirtualServer(vs) + + for _, vsrError := range vsrErrors { + lbc.recorder.Eventf(vs, api_v1.EventTypeWarning, "IgnoredVirtualServerRoute", "Ignored VirtualServerRoute %v: %v", vsrError.VirtualServerRouteNsName, vsrError.Error) + if vsrError.VirtualServerRoute != nil { + lbc.recorder.Eventf(vsrError.VirtualServerRoute, api_v1.EventTypeWarning, "Ignored", "Ignored by VirtualServer %v/%v: %v", vs.Namespace, vs.Name, vsrError.Error) + } + } + + addErr := lbc.configurator.AddOrUpdateVirtualServer(vsEx) + + eventTitle := "AddedOrUpdated" + eventType := api_v1.EventTypeNormal + eventWarningMessage := "" + + if addErr != nil { + eventTitle = "AddedOrUpdatedWithError" + eventType = api_v1.EventTypeWarning + eventWarningMessage = fmt.Sprintf("but was not applied: %v", addErr) + } + + lbc.recorder.Eventf(vs, eventType, eventTitle, "Configuration for %v was added or updated %s", key, eventWarningMessage) + for _, vsr := range vsEx.VirtualServerRoutes { + lbc.recorder.Eventf(vsr, eventType, eventTitle, "Configuration for %v/%v was added or updated %s", vsr.Namespace, vsr.Name, eventWarningMessage) + } +} + +func (lbc *LoadBalancerController) syncVirtualServerRoute(task task) { + key := task.Key + + obj, exists, err := lbc.virtualServerRouteLister.GetByKey(key) + if err != nil { + lbc.syncQueue.Requeue(task, err) + return + } + + if !exists { + glog.V(2).Infof("Deleting VirtualServerRoute: %v\n", key) + + lbc.enqueueVirtualServersForVirtualServerRouteKey(key) + return + } + + glog.V(2).Infof("Adding or Updating VirtualServerRoute: %v\n", key) + + vsr := obj.(*conf_v1alpha1.VirtualServerRoute) + + validationErr := validation.ValidateVirtualServerRoute(vsr) + if validationErr != nil { + lbc.recorder.Eventf(vsr, api_v1.EventTypeWarning, "Rejected", "VirtualServerRoute %s is invalid and was rejected: %v", key, validationErr) + } + + vsCount := lbc.enqueueVirtualServersForVirtualServerRouteKey(key) + + if vsCount == 0 { + lbc.recorder.Eventf(vsr, api_v1.EventTypeWarning, "NoVirtualServersFound", "No VirtualServer references VirtualServerRoute %s", key) } } @@ -663,12 +848,18 @@ func (lbc *LoadBalancerController) syncSecret(task task) { lbc.syncQueue.RequeueAfter(task, err, 5*time.Second) } + var virtualServers []*conf_v1alpha1.VirtualServer + if lbc.areCustomResourcesEnabled { + virtualServers = lbc.getVirtualServersForSecret(namespace, name) + glog.V(2).Infof("Found %v VirtualServers with Secret %v", len(virtualServers), key) + } + glog.V(2).Infof("Found %v Ingresses with Secret %v", len(ings), key) if !secrExists { glog.V(2).Infof("Deleting Secret: %v\n", key) - lbc.handleRegularSecretDeletion(key, ings) + lbc.handleRegularSecretDeletion(key, ings, virtualServers) if lbc.isSpecialSecret(key) { glog.Warningf("A special TLS Secret %v was removed. Retaining the Secret.", key) } @@ -681,11 +872,11 @@ func (lbc *LoadBalancerController) syncSecret(task task) { if lbc.isSpecialSecret(key) { lbc.handleSpecialSecretUpdate(secret) - // we don't return here in case the special secret is also used in Ingress resources + // we don't return here in case the special secret is also used in Ingress or VirtualServer resources. } - if len(ings) > 0 { - lbc.handleSecretUpdate(secret, ings) + if len(ings)+len(virtualServers) > 0 { + lbc.handleSecretUpdate(secret, ings, virtualServers) } } @@ -693,20 +884,23 @@ func (lbc *LoadBalancerController) isSpecialSecret(secretName string) bool { return secretName == lbc.defaultServerSecret || secretName == lbc.wildcardTLSSecret } -func (lbc *LoadBalancerController) handleRegularSecretDeletion(key string, ings []extensions.Ingress) { +func (lbc *LoadBalancerController) handleRegularSecretDeletion(key string, ings []extensions.Ingress, virtualServers []*conf_v1alpha1.VirtualServer) { eventType := api_v1.EventTypeWarning title := "Missing Secret" message := fmt.Sprintf("Secret %v was removed", key) lbc.emitEventForIngresses(eventType, title, message, ings) + lbc.emitEventForVirtualServers(eventType, title, message, virtualServers) regular, mergeable := lbc.createIngresses(ings) + virtualServerExes := lbc.virtualServersToVirtualServerExes(virtualServers) + eventType = api_v1.EventTypeNormal title = "Updated" message = fmt.Sprintf("Configuration was updated due to removed secret %v", key) - if err := lbc.configurator.DeleteSecret(key, regular, mergeable); err != nil { + if err := lbc.configurator.DeleteSecret(key, regular, mergeable, virtualServerExes); err != nil { glog.Errorf("Error when deleting Secret: %v: %v", key, err) eventType = api_v1.EventTypeWarning @@ -715,9 +909,10 @@ func (lbc *LoadBalancerController) handleRegularSecretDeletion(key string, ings } lbc.emitEventForIngresses(eventType, title, message, ings) + lbc.emitEventForVirtualServers(eventType, title, message, virtualServers) } -func (lbc *LoadBalancerController) handleSecretUpdate(secret *api_v1.Secret, ings []extensions.Ingress) { +func (lbc *LoadBalancerController) handleSecretUpdate(secret *api_v1.Secret, ings []extensions.Ingress, virtualServers []*conf_v1alpha1.VirtualServer) { secretNsName := secret.Namespace + "/" + secret.Name err := lbc.ValidateSecret(secret) @@ -726,7 +921,7 @@ func (lbc *LoadBalancerController) handleSecretUpdate(secret *api_v1.Secret, ing glog.Errorf("Couldn't validate secret %v: %v", secretNsName, err) glog.Errorf("Removing invalid secret %v", secretNsName) - lbc.handleRegularSecretDeletion(secretNsName, ings) + lbc.handleRegularSecretDeletion(secretNsName, ings, virtualServers) lbc.recorder.Eventf(secret, api_v1.EventTypeWarning, "Rejected", "%v was rejected: %v", secretNsName, err) return @@ -743,7 +938,10 @@ func (lbc *LoadBalancerController) handleSecretUpdate(secret *api_v1.Secret, ing lbc.configurator.AddOrUpdateJWKSecret(secret) } else { regular, mergeable := lbc.createIngresses(ings) - if err := lbc.configurator.AddOrUpdateTLSSecret(secret, regular, mergeable); err != nil { + + virtualServerExes := lbc.virtualServersToVirtualServerExes(virtualServers) + + if err := lbc.configurator.AddOrUpdateTLSSecret(secret, regular, mergeable, virtualServerExes); err != nil { glog.Errorf("Error when updating Secret %v: %v", secretNsName, err) lbc.recorder.Eventf(secret, api_v1.EventTypeWarning, "UpdatedWithError", "%v was updated, but not applied: %v", secretNsName, err) @@ -754,6 +952,7 @@ func (lbc *LoadBalancerController) handleSecretUpdate(secret *api_v1.Secret, ing } lbc.emitEventForIngresses(eventType, title, message, ings) + lbc.emitEventForVirtualServers(eventType, title, message, virtualServers) } func (lbc *LoadBalancerController) handleSpecialSecretUpdate(secret *api_v1.Secret) { @@ -798,6 +997,12 @@ func (lbc *LoadBalancerController) emitEventForIngresses(eventType string, title } } +func (lbc *LoadBalancerController) emitEventForVirtualServers(eventType string, title string, message string, virtualServers []*conf_v1alpha1.VirtualServer) { + for _, vs := range virtualServers { + lbc.recorder.Eventf(vs, eventType, title, message) + } +} + func (lbc *LoadBalancerController) createIngresses(ings []extensions.Ingress) (regular []configs.IngressEx, mergeable []configs.MergeableIngresses) { for i := range ings { if isMaster(&ings[i]) { @@ -919,10 +1124,18 @@ func (lbc *LoadBalancerController) EnqueueIngressForService(svc *api_v1.Service) } } +// EnqueueVirtualServersForService enqueues VirtualServers for the given service. +func (lbc *LoadBalancerController) EnqueueVirtualServersForService(service *api_v1.Service) { + virtualServers := lbc.getVirtualServersForService(service) + for _, vs := range virtualServers { + lbc.syncQueue.Enqueue(vs) + } +} + func (lbc *LoadBalancerController) getIngressesForService(svc *api_v1.Service) []extensions.Ingress { ings, err := lbc.ingressLister.GetServiceIngress(svc) if err != nil { - glog.V(3).Infof("ignoring service %v: %v", svc.Name, err) + glog.V(3).Infof("For service %v: %v", svc.Name, err) return nil } return ings @@ -943,6 +1156,181 @@ func (lbc *LoadBalancerController) getIngressForEndpoints(obj interface{}) []ext return ings } +func (lbc *LoadBalancerController) getVirtualServersForEndpoints(endpoints *api_v1.Endpoints) []*conf_v1alpha1.VirtualServer { + svcKey := fmt.Sprintf("%s/%s", endpoints.Namespace, endpoints.Name) + + svc, exists, err := lbc.svcLister.GetByKey(svcKey) + if err != nil { + glog.V(3).Infof("Error getting service %v from the cache: %v", svcKey, err) + return nil + } + if !exists { + glog.V(3).Infof("Service %v doesn't exist", svcKey) + return nil + } + + return lbc.getVirtualServersForService(svc.(*api_v1.Service)) +} + +func (lbc *LoadBalancerController) getVirtualServersForService(service *api_v1.Service) []*conf_v1alpha1.VirtualServer { + var result []*conf_v1alpha1.VirtualServer + + allVirtualServers := lbc.getVirtualServers() + + // find VirtualServers that reference VirtualServerRoutes that reference the service + virtualServerRoutes := findVirtualServerRoutesForService(lbc.getVirtualServerRoutes(), service) + for _, vsr := range virtualServerRoutes { + virtualServers := findVirtualServersForVirtualServerRoute(allVirtualServers, vsr) + result = append(result, virtualServers...) + } + + // find VirtualServers that reference the service + virtualServers := findVirtualServersForService(lbc.getVirtualServers(), service) + result = append(result, virtualServers...) + + return result +} + +func findVirtualServersForService(virtualServers []*conf_v1alpha1.VirtualServer, service *api_v1.Service) []*conf_v1alpha1.VirtualServer { + var result []*conf_v1alpha1.VirtualServer + + for _, vs := range virtualServers { + if vs.Namespace != service.Namespace { + continue + } + + isReferenced := false + for _, u := range vs.Spec.Upstreams { + if u.Service == service.Name { + isReferenced = true + break + } + } + if !isReferenced { + continue + } + + result = append(result, vs) + } + + return result +} + +func findVirtualServerRoutesForService(virtualServerRoutes []*conf_v1alpha1.VirtualServerRoute, service *api_v1.Service) []*conf_v1alpha1.VirtualServerRoute { + var result []*conf_v1alpha1.VirtualServerRoute + + for _, vsr := range virtualServerRoutes { + if vsr.Namespace != service.Namespace { + continue + } + + isReferenced := false + for _, u := range vsr.Spec.Upstreams { + if u.Service == service.Name { + isReferenced = true + break + } + } + if !isReferenced { + continue + } + + result = append(result, vsr) + } + + return result +} + +func (lbc *LoadBalancerController) getVirtualServersForSecret(secretNamespace string, secretName string) []*conf_v1alpha1.VirtualServer { + virtualServers := lbc.getVirtualServers() + return findVirtualServersForSecret(virtualServers, secretNamespace, secretName) +} + +func findVirtualServersForSecret(virtualServers []*conf_v1alpha1.VirtualServer, secretNamespace string, secretName string) []*conf_v1alpha1.VirtualServer { + var result []*conf_v1alpha1.VirtualServer + + for _, vs := range virtualServers { + if vs.Spec.TLS == nil { + continue + } + if vs.Spec.TLS.Secret == "" { + continue + } + + if vs.Namespace == secretNamespace && vs.Spec.TLS.Secret == secretName { + result = append(result, vs) + } + } + + return result +} + +func (lbc *LoadBalancerController) getVirtualServers() []*conf_v1alpha1.VirtualServer { + var virtualServers []*conf_v1alpha1.VirtualServer + + for _, obj := range lbc.virtualServerLister.List() { + vs := obj.(*conf_v1alpha1.VirtualServer) + + err := validation.ValidateVirtualServer(vs) + if err != nil { + glog.V(3).Infof("Skipping invalid VirtualServer %s/%s: %v", vs.Namespace, vs.Name, err) + continue + } + + virtualServers = append(virtualServers, vs) + } + + return virtualServers +} + +func (lbc *LoadBalancerController) getVirtualServerRoutes() []*conf_v1alpha1.VirtualServerRoute { + var virtualServerRoutes []*conf_v1alpha1.VirtualServerRoute + + for _, obj := range lbc.virtualServerRouteLister.List() { + vsr := obj.(*conf_v1alpha1.VirtualServerRoute) + + err := validation.ValidateVirtualServerRoute(vsr) + if err != nil { + glog.V(3).Infof("Skipping invalid VirtualServerRoute %s/%s: %v", vsr.Namespace, vsr.Name, err) + continue + } + + virtualServerRoutes = append(virtualServerRoutes, vsr) + } + + return virtualServerRoutes +} + +func (lbc *LoadBalancerController) enqueueVirtualServersForVirtualServerRouteKey(key string) int { + virtualServers := findVirtualServersForVirtualServerRouteKey(lbc.getVirtualServers(), key) + + for _, vs := range virtualServers { + lbc.syncQueue.Enqueue(vs) + } + + return len(virtualServers) +} + +func findVirtualServersForVirtualServerRoute(virtualServers []*conf_v1alpha1.VirtualServer, virtualServerRoute *conf_v1alpha1.VirtualServerRoute) []*conf_v1alpha1.VirtualServer { + key := fmt.Sprintf("%s/%s", virtualServerRoute.Namespace, virtualServerRoute.Name) + return findVirtualServersForVirtualServerRouteKey(virtualServers, key) +} + +func findVirtualServersForVirtualServerRouteKey(virtualServers []*conf_v1alpha1.VirtualServer, key string) []*conf_v1alpha1.VirtualServer { + var result []*conf_v1alpha1.VirtualServer + + for _, vs := range virtualServers { + for _, r := range vs.Spec.Routes { + if r.Route == key { + result = append(result, vs) + break + } + } + } + + return result +} + func (lbc *LoadBalancerController) getAndValidateSecret(secretKey string) (*api_v1.Secret, error) { secretObject, secretExists, err := lbc.secretLister.GetByKey(secretKey) if err != nil { @@ -1079,6 +1467,122 @@ func (lbc *LoadBalancerController) createIngress(ing *extensions.Ingress) (*conf return ingEx, nil } +type virtualServerRouteError struct { + VirtualServerRouteNsName string + VirtualServerRoute *conf_v1alpha1.VirtualServerRoute + Error error +} + +func newVirtualServerRouteErrorFromNsName(nsName string, err error) virtualServerRouteError { + return virtualServerRouteError{ + VirtualServerRouteNsName: nsName, + Error: err, + } +} + +func newVirtualServerRouteErrorFromVSR(virtualServerRoute *conf_v1alpha1.VirtualServerRoute, err error) virtualServerRouteError { + return virtualServerRouteError{ + VirtualServerRoute: virtualServerRoute, + VirtualServerRouteNsName: fmt.Sprintf("%s/%s", virtualServerRoute.Namespace, virtualServerRoute.Name), + Error: err, + } +} + +func (lbc *LoadBalancerController) createVirtualServer(virtualServer *conf_v1alpha1.VirtualServer) (*configs.VirtualServerEx, []virtualServerRouteError) { + virtualServerEx := configs.VirtualServerEx{ + VirtualServer: virtualServer, + } + + if virtualServer.Spec.TLS != nil && virtualServer.Spec.TLS.Secret != "" { + secretKey := virtualServer.Namespace + "/" + virtualServer.Spec.TLS.Secret + secret, err := lbc.getAndValidateSecret(secretKey) + if err != nil { + glog.Warningf("Error trying to get the secret %v for VirtualServer %v: %v", secretKey, virtualServer.Name, err) + } else { + virtualServerEx.TLSSecret = secret + } + } + + endpoints := make(map[string][]string) + + for _, u := range virtualServer.Spec.Upstreams { + endpointsKey := configs.GenerateEndpointsKey(virtualServer.Namespace, u.Service, u.Port) + endpoints[endpointsKey] = lbc.getEndpointsForService(virtualServer.Namespace, u.Service, int(u.Port)) + } + + var virtualServerRoutes []*conf_v1alpha1.VirtualServerRoute + var virtualServerRouteErrors []virtualServerRouteError + + // gather all referenced VirtualServerRoutes + for _, r := range virtualServer.Spec.Routes { + if r.Route == "" { + continue + } + + vsrKey := r.Route + + // if route is defined without a namespace, use the namespace of VirtualServer. + if !strings.Contains(r.Route, "/") { + vsrKey = fmt.Sprintf("%s/%s", virtualServer.Namespace, r.Route) + } + + obj, exists, err := lbc.virtualServerRouteLister.GetByKey(vsrKey) + if err != nil { + glog.Warningf("Failed to get VirtualServerRoute %s for VirtualServer %s/%s: %v", vsrKey, virtualServer.Namespace, virtualServer.Name, err) + virtualServerRouteErrors = append(virtualServerRouteErrors, newVirtualServerRouteErrorFromNsName(vsrKey, err)) + continue + } + + if !exists { + glog.Warningf("VirtualServer %s/%s references VirtualServerRoute %s that doesn't exist", virtualServer.Name, virtualServer.Namespace, vsrKey) + virtualServerRouteErrors = append(virtualServerRouteErrors, newVirtualServerRouteErrorFromNsName(vsrKey, errors.New("VirtualServerRoute doesn't exist"))) + continue + } + + vsr := obj.(*conf_v1alpha1.VirtualServerRoute) + + err = validation.ValidateVirtualServerRouteForVirtualServer(vsr, virtualServer.Spec.Host, r.Path) + if err != nil { + glog.Warningf("VirtualServer %s/%s references invalid VirtualServerRoute %s: %v", virtualServer.Name, virtualServer.Namespace, vsrKey, err) + virtualServerRouteErrors = append(virtualServerRouteErrors, newVirtualServerRouteErrorFromVSR(vsr, err)) + continue + } + + virtualServerRoutes = append(virtualServerRoutes, vsr) + + for _, u := range vsr.Spec.Upstreams { + endpointsKey := configs.GenerateEndpointsKey(vsr.Namespace, u.Service, u.Port) + endpoints[endpointsKey] = lbc.getEndpointsForService(vsr.Namespace, u.Service, int(u.Port)) + } + } + + virtualServerEx.Endpoints = endpoints + virtualServerEx.VirtualServerRoutes = virtualServerRoutes + + return &virtualServerEx, virtualServerRouteErrors +} + +func (lbc *LoadBalancerController) getEndpointsForService(namespace string, name string, port int) []string { + backend := &extensions.IngressBackend{ + ServiceName: name, + ServicePort: intstr.FromInt(port), + } + + svc, err := lbc.getServiceForIngressBackend(backend, namespace) + if err != nil { + glog.Warningf("Error getting service %v: %v", name, err) + return nil + } + + endps, _, err := lbc.getEndpointsForIngressBackend(backend, namespace, svc) + if err != nil { + glog.Warningf("Error retrieving endpoints for the service %v: %v", name, err) + return nil + } + + return endps +} + func (lbc *LoadBalancerController) getPodsForIngressBackend(svc *api_v1.Service, namespace string) *api_v1.PodList { pods, err := lbc.client.CoreV1().Pods(svc.Namespace).List(meta_v1.ListOptions{LabelSelector: labels.Set(svc.Spec.Selector).String()}) if err != nil { diff --git a/internal/k8s/controller_test.go b/internal/k8s/controller_test.go index 89b3097134..1ebf811f9c 100644 --- a/internal/k8s/controller_test.go +++ b/internal/k8s/controller_test.go @@ -7,11 +7,13 @@ import ( "time" "unsafe" + "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" "github.com/nginxinc/kubernetes-ingress/internal/metrics/collectors" "github.com/nginxinc/kubernetes-ingress/internal/configs" "github.com/nginxinc/kubernetes-ingress/internal/configs/version1" "github.com/nginxinc/kubernetes-ingress/internal/nginx" + conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" v1 "k8s.io/api/core/v1" extensions "k8s.io/api/extensions/v1beta1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -631,7 +633,7 @@ func getMergableDefaults() (cafeMaster, coffeeMinion, teaMinion extensions.Ingre cafeMasterIngEx, _ := lbc.createIngress(&cafeMaster) ingExMap["default-cafe-master"] = cafeMasterIngEx - cnf := configs.NewConfigurator(&nginx.LocalManager{}, &configs.StaticConfigParams{}, &configs.ConfigParams{}, &version1.TemplateExecutor{}, false, false) + cnf := configs.NewConfigurator(&nginx.LocalManager{}, &configs.StaticConfigParams{}, &configs.ConfigParams{}, &version1.TemplateExecutor{}, &version2.TemplateExecutor{}, false, false) // edit private field ingresses to use in testing pointerVal := reflect.ValueOf(cnf) @@ -845,7 +847,7 @@ func TestFindProbeForPods(t *testing.T) { func TestGetServicePortForIngressPort(t *testing.T) { fakeClient := fake.NewSimpleClientset() - cnf := configs.NewConfigurator(&nginx.LocalManager{}, &configs.StaticConfigParams{}, &configs.ConfigParams{}, &version1.TemplateExecutor{}, false, false) + cnf := configs.NewConfigurator(&nginx.LocalManager{}, &configs.StaticConfigParams{}, &configs.ConfigParams{}, &version1.TemplateExecutor{}, &version2.TemplateExecutor{}, false, false) lbc := LoadBalancerController{ client: fakeClient, ingressClass: "nginx", @@ -992,11 +994,17 @@ func TestFindIngressesForSecret(t *testing.T) { templateExecutor, err := version1.NewTemplateExecutor("../configs/version1/nginx-plus.tmpl", "../configs/version1/nginx-plus.ingress.tmpl") if err != nil { - t.Fatalf("templateExecuter could not start: %v", err) + t.Fatalf("templateExecutor could not start: %v", err) } + + templateExecutorV2, err := version2.NewTemplateExecutor("../configs/version2/nginx-plus.virtualserver.tmpl") + if err != nil { + t.Fatalf("templateExecutorV2 could not start: %v", err) + } + manager := nginx.NewFakeManager("/etc/nginx") - cnf := configs.NewConfigurator(manager, &configs.StaticConfigParams{}, &configs.ConfigParams{}, templateExecutor, false, false) + cnf := configs.NewConfigurator(manager, &configs.StaticConfigParams{}, &configs.ConfigParams{}, templateExecutor, templateExecutorV2, false, false) lbc := LoadBalancerController{ client: fakeClient, ingressClass: "nginx", @@ -1177,11 +1185,17 @@ func TestFindIngressesForSecretWithMinions(t *testing.T) { templateExecutor, err := version1.NewTemplateExecutor("../configs/version1/nginx-plus.tmpl", "../configs/version1/nginx-plus.ingress.tmpl") if err != nil { - t.Fatalf("templateExecuter could not start: %v", err) + t.Fatalf("templateExecutor could not start: %v", err) } + + templateExecutorV2, err := version2.NewTemplateExecutor("../configs/version2/nginx-plus.virtualserver.tmpl") + if err != nil { + t.Fatalf("templateExecutorV2 could not start: %v", err) + } + manager := nginx.NewFakeManager("/etc/nginx") - cnf := configs.NewConfigurator(manager, &configs.StaticConfigParams{}, &configs.ConfigParams{}, templateExecutor, false, false) + cnf := configs.NewConfigurator(manager, &configs.StaticConfigParams{}, &configs.ConfigParams{}, templateExecutor, templateExecutorV2, false, false) lbc := LoadBalancerController{ client: fakeClient, ingressClass: "nginx", @@ -1252,5 +1266,244 @@ func TestFindIngressesForSecretWithMinions(t *testing.T) { } }) } +} + +func TestFindVirtualServersForService(t *testing.T) { + vs1 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-1", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + Upstreams: []conf_v1alpha1.Upstream{ + { + Service: "test-service", + }, + }, + }, + } + vs2 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-2", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + Upstreams: []conf_v1alpha1.Upstream{ + { + Service: "some-service", + }, + }, + }, + } + vs3 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-3", + Namespace: "ns-2", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + Upstreams: []conf_v1alpha1.Upstream{ + { + Service: "test-service", + }, + }, + }, + } + virtualServers := []*conf_v1alpha1.VirtualServer{&vs1, &vs2, &vs3} + + service := v1.Service{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "test-service", + Namespace: "ns-1", + }, + } + + expected := []*conf_v1alpha1.VirtualServer{&vs1} + + result := findVirtualServersForService(virtualServers, &service) + if !reflect.DeepEqual(result, expected) { + t.Errorf("findVirtualServersForService returned %v but expected %v", result, expected) + } +} + +func TestFindVirtualServerRoutesForService(t *testing.T) { + vsr1 := conf_v1alpha1.VirtualServerRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vsr-1", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerRouteSpec{ + Upstreams: []conf_v1alpha1.Upstream{ + { + Service: "test-service", + }, + }, + }, + } + vsr2 := conf_v1alpha1.VirtualServerRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vsr-2", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerRouteSpec{ + Upstreams: []conf_v1alpha1.Upstream{ + { + Service: "some-service", + }, + }, + }, + } + vsr3 := conf_v1alpha1.VirtualServerRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vrs-3", + Namespace: "ns-2", + }, + Spec: conf_v1alpha1.VirtualServerRouteSpec{ + Upstreams: []conf_v1alpha1.Upstream{ + { + Service: "test-service", + }, + }, + }, + } + virtualServerRoutes := []*conf_v1alpha1.VirtualServerRoute{&vsr1, &vsr2, &vsr3} + + service := v1.Service{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "test-service", + Namespace: "ns-1", + }, + } + expected := []*conf_v1alpha1.VirtualServerRoute{&vsr1} + + result := findVirtualServerRoutesForService(virtualServerRoutes, &service) + if !reflect.DeepEqual(result, expected) { + t.Errorf("findVirtualServerRoutesForService returned %v but expected %v", result, expected) + } +} + +func TestFindVirtualServersForSecret(t *testing.T) { + vs1 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-1", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + TLS: nil, + }, + } + vs2 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-2", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + TLS: &conf_v1alpha1.TLS{ + Secret: "", + }, + }, + } + vs3 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-3", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + TLS: &conf_v1alpha1.TLS{ + Secret: "some-secret", + }, + }, + } + vs4 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-4", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + TLS: &conf_v1alpha1.TLS{ + Secret: "test-secret", + }, + }, + } + vs5 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-5", + Namespace: "ns-2", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + TLS: &conf_v1alpha1.TLS{ + Secret: "test-secret", + }, + }, + } + + virtualServers := []*conf_v1alpha1.VirtualServer{&vs1, &vs2, &vs3, &vs4, &vs5} + + expected := []*conf_v1alpha1.VirtualServer{&vs4} + + result := findVirtualServersForSecret(virtualServers, "ns-1", "test-secret") + if !reflect.DeepEqual(result, expected) { + t.Errorf("findVirtualServersForSecret returned %v but expected %v", result, expected) + } +} + +func TestFindVirtualServersForVirtualServerRoute(t *testing.T) { + vs1 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-1", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + Routes: []conf_v1alpha1.Route{ + { + Path: "/", + Route: "default/test", + }, + }, + }, + } + vs2 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-2", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + Routes: []conf_v1alpha1.Route{ + { + Path: "/", + Route: "some-ns/test", + }, + }, + }, + } + vs3 := conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "vs-3", + Namespace: "ns-1", + }, + Spec: conf_v1alpha1.VirtualServerSpec{ + Routes: []conf_v1alpha1.Route{ + { + Path: "/", + Route: "default/test", + }, + }, + }, + } + + vsr := conf_v1alpha1.VirtualServerRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + } + + virtualServers := []*conf_v1alpha1.VirtualServer{&vs1, &vs2, &vs3} + + expected := []*conf_v1alpha1.VirtualServer{&vs1, &vs3} + + result := findVirtualServersForVirtualServerRoute(virtualServers, &vsr) + if !reflect.DeepEqual(result, expected) { + t.Errorf("findVirtualServersForVirtualServerRoute returned %v but expected %v", result, expected) + } } diff --git a/internal/k8s/handlers.go b/internal/k8s/handlers.go index 0e84cb085b..1db64e3d6c 100644 --- a/internal/k8s/handlers.go +++ b/internal/k8s/handlers.go @@ -5,9 +5,11 @@ import ( "sort" "github.com/golang/glog" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" "k8s.io/client-go/tools/cache" + + conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" ) // createConfigMapHandlers builds the handler funcs for config maps @@ -199,6 +201,10 @@ func createServiceHandlers(lbc *LoadBalancerController) cache.ResourceEventHandl } glog.V(3).Infof("Adding service: %v", svc.Name) lbc.EnqueueIngressForService(svc) + + if lbc.areCustomResourcesEnabled { + lbc.EnqueueVirtualServersForService(svc) + } }, DeleteFunc: func(obj interface{}) { svc, isSvc := obj.(*v1.Service) @@ -222,6 +228,10 @@ func createServiceHandlers(lbc *LoadBalancerController) cache.ResourceEventHandl glog.V(3).Infof("Removing service: %v", svc.Name) lbc.EnqueueIngressForService(svc) + if lbc.areCustomResourcesEnabled { + lbc.EnqueueVirtualServersForService(svc) + } + }, UpdateFunc: func(old, cur interface{}) { if !reflect.DeepEqual(old, cur) { @@ -234,6 +244,10 @@ func createServiceHandlers(lbc *LoadBalancerController) cache.ResourceEventHandl if hasServiceChanges(oldSvc, curSvc) { glog.V(3).Infof("Service %v changed, syncing", curSvc.Name) lbc.EnqueueIngressForService(curSvc) + + if lbc.areCustomResourcesEnabled { + lbc.EnqueueVirtualServersForService(curSvc) + } } } }, @@ -290,3 +304,71 @@ func hasServicePortChanges(oldServicePorts []v1.ServicePort, curServicePorts []v } return false } + +func createVirtualServerHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs { + return cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + vs := obj.(*conf_v1alpha1.VirtualServer) + glog.V(3).Infof("Adding VirtualServer: %v", vs.Name) + lbc.AddSyncQueue(vs) + }, + DeleteFunc: func(obj interface{}) { + vs, isVs := obj.(*conf_v1alpha1.VirtualServer) + if !isVs { + deletedState, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + glog.V(3).Infof("Error received unexpected object: %v", obj) + return + } + vs, ok = deletedState.Obj.(*conf_v1alpha1.VirtualServer) + if !ok { + glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-VirtualServer object: %v", deletedState.Obj) + return + } + } + glog.V(3).Infof("Removing VirtualServer: %v", vs.Name) + lbc.AddSyncQueue(vs) + }, + UpdateFunc: func(old, cur interface{}) { + curVs := cur.(*conf_v1alpha1.VirtualServer) + if !reflect.DeepEqual(old, cur) { + glog.V(3).Infof("VirtualServer %v changed, syncing", curVs.Name) + lbc.AddSyncQueue(curVs) + } + }, + } +} + +func createVirtualServerRouteHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs { + return cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + vsr := obj.(*conf_v1alpha1.VirtualServerRoute) + glog.V(3).Infof("Adding VirtualServerRoute: %v", vsr.Name) + lbc.AddSyncQueue(vsr) + }, + DeleteFunc: func(obj interface{}) { + vsr, isVsr := obj.(*conf_v1alpha1.VirtualServerRoute) + if !isVsr { + deletedState, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + glog.V(3).Infof("Error received unexpected object: %v", obj) + return + } + vsr, ok = deletedState.Obj.(*conf_v1alpha1.VirtualServerRoute) + if !ok { + glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-VirtualServerRoute object: %v", deletedState.Obj) + return + } + } + glog.V(3).Infof("Removing VirtualServerRoute: %v", vsr.Name) + lbc.AddSyncQueue(vsr) + }, + UpdateFunc: func(old, cur interface{}) { + curVsr := cur.(*conf_v1alpha1.VirtualServerRoute) + if !reflect.DeepEqual(old, cur) { + glog.V(3).Infof("VirtualServerRoute %v changed, syncing", curVsr.Name) + lbc.AddSyncQueue(curVsr) + } + }, + } +} diff --git a/internal/k8s/task_queue.go b/internal/k8s/task_queue.go index 065a6121c1..49773b779f 100644 --- a/internal/k8s/task_queue.go +++ b/internal/k8s/task_queue.go @@ -5,7 +5,8 @@ import ( "time" "github.com/golang/glog" - "k8s.io/api/core/v1" + conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/util/workqueue" @@ -107,6 +108,10 @@ const ( secret // service resource service + // virtualserver resource + virtualserver + // virtualServeRoute resource + virtualServerRoute ) // task is an element of a taskQueue @@ -134,6 +139,10 @@ func newTask(key string, obj interface{}) (task, error) { k = secret case *v1.Service: k = service + case *conf_v1alpha1.VirtualServer: + k = virtualserver + case *conf_v1alpha1.VirtualServerRoute: + k = virtualServerRoute default: return task{}, fmt.Errorf("Unknow type: %v", t) } diff --git a/pkg/apis/configuration/register.go b/pkg/apis/configuration/register.go new file mode 100644 index 0000000000..04d4f2973a --- /dev/null +++ b/pkg/apis/configuration/register.go @@ -0,0 +1,5 @@ +package configuration + +const ( + GroupName = "k8s.nginx.org" +) diff --git a/pkg/apis/configuration/v1alpha1/doc.go b/pkg/apis/configuration/v1alpha1/doc.go new file mode 100644 index 0000000000..ab23aacb55 --- /dev/null +++ b/pkg/apis/configuration/v1alpha1/doc.go @@ -0,0 +1,5 @@ +// +k8s:deepcopy-gen=package +// +groupName=k8s.nginx.org + +// Package v1alpha1 is the v1alpha1 version of the API. +package v1alpha1 diff --git a/pkg/apis/configuration/v1alpha1/register.go b/pkg/apis/configuration/v1alpha1/register.go new file mode 100644 index 0000000000..9c1ea9491e --- /dev/null +++ b/pkg/apis/configuration/v1alpha1/register.go @@ -0,0 +1,38 @@ +package v1alpha1 + +import ( + "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these object. +var SchemeGroupVersion = schema.GroupVersion{Group: configuration.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back 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 +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &VirtualServer{}, + &VirtualServerList{}, + &VirtualServerRoute{}, + &VirtualServerRouteList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/configuration/v1alpha1/types.go b/pkg/apis/configuration/v1alpha1/types.go new file mode 100644 index 0000000000..2d3614857b --- /dev/null +++ b/pkg/apis/configuration/v1alpha1/types.go @@ -0,0 +1,107 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// VirtualServer defines the VirtualServer resource. +type VirtualServer struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec VirtualServerSpec `json:"spec"` +} + +// VirtualServerSpec is the spec of the VirtualServer resource. +type VirtualServerSpec struct { + Host string `json:"host"` + TLS *TLS `json:"tls"` + Upstreams []Upstream `json:"upstreams"` + Routes []Route `json:"routes"` +} + +// Upstream defines an upstream. +type Upstream struct { + Name string `json:"name"` + Service string `json:"service"` + Port uint16 `json:"port"` +} + +// Route defines a route. +type Route struct { + Path string `json:"path"` + Upstream string `json:"upstream"` + Splits []Split `json:"splits"` + Rules *Rules `json:"rules"` + Route string `json:"route"` +} + +// Split defines a split. +type Split struct { + Weight int `json:"weight"` + Upstream string `json:"upstream"` +} + +// Rules defines rules. +type Rules struct { + Conditions []Condition `json:"conditions"` + Matches []Match `json:"matches"` + DefaultUpstream string `json:"defaultUpstream"` +} + +// Condition defines a condition in a MatchRule. +type Condition struct { + Header string `json:"header"` + Cookie string `json:"cookie"` + Argument string `json:"argument"` + Variable string `json:"variable"` +} + +// Match defines a match in a MatchRule. +type Match struct { + Values []string `json:"values"` + Upstream string `json:"upstream"` +} + +// TLS defines TLS configuration for a VirtualServer. +type TLS struct { + Secret string `json:"secret"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// VirtualServerList is a list of the VirtualServer resources. +type VirtualServerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []VirtualServer `json:"items"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type VirtualServerRoute struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec VirtualServerRouteSpec `json:"spec"` +} + +type VirtualServerRouteSpec struct { + Host string `json:"host"` + Upstreams []Upstream `json:"upstreams"` + Subroutes []Route `json:"subroutes"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type VirtualServerRouteList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []VirtualServerRoute `json:"items"` +} diff --git a/pkg/apis/configuration/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/configuration/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..580e690d87 --- /dev/null +++ b/pkg/apis/configuration/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,329 @@ +// +build !ignore_autogenerated + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Match) DeepCopyInto(out *Match) { + *out = *in + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Match. +func (in *Match) DeepCopy() *Match { + if in == nil { + return nil + } + out := new(Match) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Route) DeepCopyInto(out *Route) { + *out = *in + if in.Splits != nil { + in, out := &in.Splits, &out.Splits + *out = make([]Split, len(*in)) + copy(*out, *in) + } + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = new(Rules) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Route. +func (in *Route) DeepCopy() *Route { + if in == nil { + return nil + } + out := new(Route) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Rules) DeepCopyInto(out *Rules) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + copy(*out, *in) + } + if in.Matches != nil { + in, out := &in.Matches, &out.Matches + *out = make([]Match, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rules. +func (in *Rules) DeepCopy() *Rules { + if in == nil { + return nil + } + out := new(Rules) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Split) DeepCopyInto(out *Split) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Split. +func (in *Split) DeepCopy() *Split { + if in == nil { + return nil + } + out := new(Split) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLS) DeepCopyInto(out *TLS) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLS. +func (in *TLS) DeepCopy() *TLS { + if in == nil { + return nil + } + out := new(TLS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Upstream) DeepCopyInto(out *Upstream) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Upstream. +func (in *Upstream) DeepCopy() *Upstream { + if in == nil { + return nil + } + out := new(Upstream) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualServer) DeepCopyInto(out *VirtualServer) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServer. +func (in *VirtualServer) DeepCopy() *VirtualServer { + if in == nil { + return nil + } + out := new(VirtualServer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VirtualServer) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualServerList) DeepCopyInto(out *VirtualServerList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VirtualServer, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServerList. +func (in *VirtualServerList) DeepCopy() *VirtualServerList { + if in == nil { + return nil + } + out := new(VirtualServerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VirtualServerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualServerRoute) DeepCopyInto(out *VirtualServerRoute) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServerRoute. +func (in *VirtualServerRoute) DeepCopy() *VirtualServerRoute { + if in == nil { + return nil + } + out := new(VirtualServerRoute) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VirtualServerRoute) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualServerRouteList) DeepCopyInto(out *VirtualServerRouteList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VirtualServerRoute, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServerRouteList. +func (in *VirtualServerRouteList) DeepCopy() *VirtualServerRouteList { + if in == nil { + return nil + } + out := new(VirtualServerRouteList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VirtualServerRouteList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualServerRouteSpec) DeepCopyInto(out *VirtualServerRouteSpec) { + *out = *in + if in.Upstreams != nil { + in, out := &in.Upstreams, &out.Upstreams + *out = make([]Upstream, len(*in)) + copy(*out, *in) + } + if in.Subroutes != nil { + in, out := &in.Subroutes, &out.Subroutes + *out = make([]Route, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServerRouteSpec. +func (in *VirtualServerRouteSpec) DeepCopy() *VirtualServerRouteSpec { + if in == nil { + return nil + } + out := new(VirtualServerRouteSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualServerSpec) DeepCopyInto(out *VirtualServerSpec) { + *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLS) + **out = **in + } + if in.Upstreams != nil { + in, out := &in.Upstreams, &out.Upstreams + *out = make([]Upstream, len(*in)) + copy(*out, *in) + } + if in.Routes != nil { + in, out := &in.Routes, &out.Routes + *out = make([]Route, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServerSpec. +func (in *VirtualServerSpec) DeepCopy() *VirtualServerSpec { + if in == nil { + return nil + } + out := new(VirtualServerSpec) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/configuration/validation/validation.go b/pkg/apis/configuration/validation/validation.go new file mode 100644 index 0000000000..0014e6fdb3 --- /dev/null +++ b/pkg/apis/configuration/validation/validation.go @@ -0,0 +1,476 @@ +package validation + +import ( + "fmt" + "regexp" + "strings" + + "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +// ValidateVirtualServer validates a VirtualServer. +func ValidateVirtualServer(virtualServer *v1alpha1.VirtualServer) error { + allErrs := validateVirtualServerSpec(&virtualServer.Spec, field.NewPath("spec")) + return allErrs.ToAggregate() +} + +// validateVirtualServerSpec validates a VirtualServerSpec. +func validateVirtualServerSpec(spec *v1alpha1.VirtualServerSpec, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + allErrs = append(allErrs, validateHost(spec.Host, fieldPath.Child("host"))...) + allErrs = append(allErrs, validateTLS(spec.TLS, fieldPath.Child("tls"))...) + + upstreamErrs, upstreamNames := validateUpstreams(spec.Upstreams, fieldPath.Child("upstreams")) + allErrs = append(allErrs, upstreamErrs...) + + allErrs = append(allErrs, validateVirtualServerRoutes(spec.Routes, fieldPath.Child("routes"), upstreamNames)...) + + return allErrs +} + +func validateHost(host string, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if host == "" { + return append(allErrs, field.Required(fieldPath, "")) + } + + for _, msg := range validation.IsDNS1123Subdomain(host) { + allErrs = append(allErrs, field.Invalid(fieldPath, host, msg)) + } + + return allErrs +} + +func validateTLS(tls *v1alpha1.TLS, fieldPath *field.Path) field.ErrorList { + if tls == nil { + // valid case - tls is not defined + return field.ErrorList{} + } + + return validateSecretName(tls.Secret, fieldPath.Child("secret")) +} + +// validateSecretName checks if a secret name is valid. +// It performs the same validation as ValidateSecretName from k8s.io/kubernetes/pkg/apis/core/validation/validation.go. +func validateSecretName(name string, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if name == "" { + return append(allErrs, field.Required(fieldPath, "")) + } + + for _, msg := range validation.IsDNS1123Subdomain(name) { + allErrs = append(allErrs, field.Invalid(fieldPath, name, msg)) + } + + return allErrs +} + +func validateUpstreams(upstreams []v1alpha1.Upstream, fieldPath *field.Path) (allErrs field.ErrorList, upstreamNames sets.String) { + allErrs = field.ErrorList{} + upstreamNames = sets.String{} + + for i, u := range upstreams { + idxPath := fieldPath.Index(i) + + upstreamErrors := validateUpstreamName(u.Name, idxPath.Child("name")) + if len(upstreamErrors) > 0 { + allErrs = append(allErrs, upstreamErrors...) + } else if upstreamNames.Has(u.Name) { + allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), u.Name)) + } else { + upstreamNames.Insert(u.Name) + } + + allErrs = append(allErrs, validateServiceName(u.Service, idxPath.Child("service"))...) + + for _, msg := range validation.IsValidPortNum(int(u.Port)) { + allErrs = append(allErrs, field.Invalid(idxPath.Child("port"), u.Port, msg)) + } + } + + return allErrs, upstreamNames +} + +// validateUpstreamName checks is an upstream name is valid. +// The rules for NGINX upstream names are less strict than IsDNS1035Label. +// However, it is convenient to enforce IsDNS1035Label in the yaml for +// the names of upstreams. +func validateUpstreamName(name string, fieldPath *field.Path) field.ErrorList { + return validateDNS1035Label(name, fieldPath) +} + +// validateServiceName checks if a service name is valid. +// It performs the same validation as ValidateServiceName from k8s.io/kubernetes/pkg/apis/core/validation/validation.go. +func validateServiceName(name string, fieldPath *field.Path) field.ErrorList { + return validateDNS1035Label(name, fieldPath) +} + +func validateDNS1035Label(name string, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if name == "" { + return append(allErrs, field.Required(fieldPath, "")) + } + + for _, msg := range validation.IsDNS1035Label(name) { + allErrs = append(allErrs, field.Invalid(fieldPath, name, msg)) + } + + return allErrs +} + +func validateVirtualServerRoutes(routes []v1alpha1.Route, fieldPath *field.Path, upstreamNames sets.String) field.ErrorList { + allErrs := field.ErrorList{} + + allPaths := sets.String{} + + for i, r := range routes { + idxPath := fieldPath.Index(i) + + isRouteFieldForbidden := false + routeErrs := validateRoute(r, idxPath, upstreamNames, isRouteFieldForbidden) + if len(routeErrs) > 0 { + allErrs = append(allErrs, routeErrs...) + } else if allPaths.Has(r.Path) { + allErrs = append(allErrs, field.Duplicate(idxPath.Child("path"), r.Path)) + } else { + allPaths.Insert(r.Path) + } + } + + return allErrs +} + +func validateRoute(route v1alpha1.Route, fieldPath *field.Path, upstreamNames sets.String, isRouteFieldForbidden bool) field.ErrorList { + allErrs := field.ErrorList{} + + allErrs = append(allErrs, validatePath(route.Path, fieldPath.Child("path"))...) + + fieldCount := 0 + + if route.Upstream != "" { + allErrs = append(allErrs, validateReferencedUpstream(route.Upstream, fieldPath.Child("upstream"), upstreamNames)...) + fieldCount++ + } + + if len(route.Splits) > 0 { + allErrs = append(allErrs, validateSplits(route.Splits, fieldPath.Child("splits"), upstreamNames)...) + fieldCount++ + } + + if route.Rules != nil { + allErrs = append(allErrs, validateRules(route.Rules, fieldPath.Child("rules"), upstreamNames)...) + fieldCount++ + } + + if route.Route != "" { + if isRouteFieldForbidden { + allErrs = append(allErrs, field.Forbidden(fieldPath.Child("route"), "is not allowed")) + } else { + allErrs = append(allErrs, validateRouteField(route.Route, fieldPath.Child("route"))...) + fieldCount++ + } + } + + if fieldCount != 1 { + msg := "must specify exactly one of: `upstream`, `splits`, `rules` or `route`" + if isRouteFieldForbidden { + msg = "must specify exactly one of: `upstream`, `splits` or `rules`" + } + + allErrs = append(allErrs, field.Invalid(fieldPath, "", msg)) + } + + return allErrs +} + +func validateRouteField(value string, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + for _, msg := range validation.IsQualifiedName(value) { + allErrs = append(allErrs, field.Invalid(fieldPath, value, msg)) + } + + return allErrs +} + +func validateReferencedUpstream(name string, fieldPath *field.Path, upstreamNames sets.String) field.ErrorList { + allErrs := field.ErrorList{} + + upstreamErrs := validateUpstreamName(name, fieldPath) + if len(upstreamErrs) > 0 { + allErrs = append(allErrs, upstreamErrs...) + } else if !upstreamNames.Has(name) { + allErrs = append(allErrs, field.NotFound(fieldPath, name)) + } + + return allErrs +} + +func validateSplits(splits []v1alpha1.Split, fieldPath *field.Path, upstreamNames sets.String) field.ErrorList { + allErrs := field.ErrorList{} + + if len(splits) < 2 { + return append(allErrs, field.Invalid(fieldPath, "", "must include at least 2 splits")) + } + + totalWeight := 0 + + for i, s := range splits { + idxPath := fieldPath.Index(i) + + for _, msg := range validation.IsInRange(s.Weight, 1, 99) { + allErrs = append(allErrs, field.Invalid(idxPath.Child("weight"), s.Weight, msg)) + } + + allErrs = append(allErrs, validateReferencedUpstream(s.Upstream, idxPath.Child("upstream"), upstreamNames)...) + + totalWeight += s.Weight + } + + if totalWeight != 100 { + allErrs = append(allErrs, field.Invalid(fieldPath, "", "the sum of the weights of all splits must be equal to 100")) + } + + return allErrs +} + +// For now, we only support prefix-based NGINX locations. For example, location /abc { ... }. +const pathFmt = `/[^\s{};]*` +const pathErrMsg = "must start with / and must not include any whitespace character, `{`, `}` or `;`" + +var pathRegexp = regexp.MustCompile("^" + pathFmt + "$") + +func validatePath(path string, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if path == "" { + return append(allErrs, field.Required(fieldPath, "")) + } + + if !pathRegexp.MatchString(path) { + msg := validation.RegexError(pathErrMsg, pathFmt, "/", "/path", "/path/subpath-123") + return append(allErrs, field.Invalid(fieldPath, path, msg)) + } + + return allErrs +} + +func validateRules(rules *v1alpha1.Rules, fieldPath *field.Path, upstreamNames sets.String) field.ErrorList { + allErrs := field.ErrorList{} + + if len(rules.Conditions) == 0 { + allErrs = append(allErrs, field.Required(fieldPath.Child("conditions"), "must specify at least one condition")) + } else { + for i, c := range rules.Conditions { + allErrs = append(allErrs, validateCondition(c, fieldPath.Child("conditions").Index(i))...) + } + } + + if len(rules.Matches) == 0 { + allErrs = append(allErrs, field.Required(fieldPath.Child("matches"), "must specify at least one match")) + } else { + for i, m := range rules.Matches { + allErrs = append(allErrs, validateMatch(m, fieldPath.Child("matches").Index(i), len(rules.Conditions), upstreamNames)...) + } + } + + allErrs = append(allErrs, validateReferencedUpstream(rules.DefaultUpstream, fieldPath.Child("defaultUpstream"), upstreamNames)...) + + return allErrs +} + +func validateCondition(condition v1alpha1.Condition, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + fieldCount := 0 + + if condition.Header != "" { + for _, msg := range validation.IsHTTPHeaderName(condition.Header) { + allErrs = append(allErrs, field.Invalid(fieldPath.Child("header"), condition.Header, msg)) + } + fieldCount++ + } + + if condition.Cookie != "" { + for _, msg := range isCookieName(condition.Cookie) { + allErrs = append(allErrs, field.Invalid(fieldPath.Child("cookie"), condition.Cookie, msg)) + } + fieldCount++ + } + + if condition.Argument != "" { + for _, msg := range isArgumentName(condition.Argument) { + allErrs = append(allErrs, field.Invalid(fieldPath.Child("argument"), condition.Argument, msg)) + } + fieldCount++ + } + + if condition.Variable != "" { + allErrs = append(allErrs, validateVariableName(condition.Variable, fieldPath.Child("variable"))...) + fieldCount++ + } + + if fieldCount != 1 { + allErrs = append(allErrs, field.Invalid(fieldPath, "", "must specify exactly one of: `header`, `cookie`, `argument` or `variable`")) + } + + return allErrs +} + +const cookieNameFmt string = "[_A-Za-z0-9]+" +const cookieNameErrMsg string = "a valid cookie name must consist of alphanumeric characters or '_'" + +var cookieNameRegexp = regexp.MustCompile("^" + cookieNameFmt + "$") + +func isCookieName(value string) []string { + if !cookieNameRegexp.MatchString(value) { + return []string{validation.RegexError(cookieNameErrMsg, cookieNameFmt, "my_cookie_123")} + } + return nil +} + +const argumentNameFmt string = "[_A-Za-z0-9]+" +const argumentNameErrMsg string = "a valid argument name must consist of alphanumeric characters or '_'" + +var argumentNameRegexp = regexp.MustCompile("^" + argumentNameFmt + "$") + +func isArgumentName(value string) []string { + if !argumentNameRegexp.MatchString(value) { + return []string{validation.RegexError(argumentNameErrMsg, argumentNameFmt, "argument_123")} + } + return nil +} + +// validVariableNames includes NGINX variables allowed to be used in conditions. +// Not all NGINX variables are allowed. The full list of NGINX variables is at https://nginx.org/en/docs/varindex.html +var validVariableNames = map[string]bool{ + "$args": true, + "$http2": true, + "$https": true, + "$remote_addr": true, + "$remote_port": true, + "$query_string": true, + "$request": true, + "$request_body": true, + "$request_uri": true, + "$request_method": true, + "$scheme": true, +} + +func validateVariableName(name string, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if !strings.HasPrefix(name, "$") { + return append(allErrs, field.Invalid(fieldPath, name, "must start with `$`")) + } + + if _, exists := validVariableNames[name]; !exists { + return append(allErrs, field.Invalid(fieldPath, name, "is not allowed or is not an NGINX variable")) + } + + return allErrs +} + +func validateMatch(match v1alpha1.Match, fieldPath *field.Path, conditionsCount int, upstreamNames sets.String) field.ErrorList { + allErrs := field.ErrorList{} + + if len(match.Values) != conditionsCount { + msg := fmt.Sprintf("must specify %d values (same as the number of conditions)", conditionsCount) + allErrs = append(allErrs, field.Invalid(fieldPath.Child("values"), "", msg)) + } + + for i, v := range match.Values { + for _, msg := range isValidMatchValue(v) { + allErrs = append(allErrs, field.Invalid(fieldPath.Child("values").Index(i), v, msg)) + } + } + + allErrs = append(allErrs, validateReferencedUpstream(match.Upstream, fieldPath.Child("upstream"), upstreamNames)...) + + return allErrs +} + +const matchValueFmt string = `([^"\\]|\\.)*` +const matchValueErrMsg string = `a valid value must have all '"' (double quotes) escaped and must not end with an unescaped '\' (backslash)` + +var matchValueRegexp = regexp.MustCompile("^" + matchValueFmt + "$") + +func isValidMatchValue(value string) []string { + if !matchValueRegexp.MatchString(value) { + return []string{validation.RegexError(matchValueErrMsg, matchValueFmt, "value-123")} + } + return nil +} + +// ValidateVirtualServerRoute validates a VirtualServerRoute. +func ValidateVirtualServerRoute(virtualServerRoute *v1alpha1.VirtualServerRoute) error { + allErrs := validateVirtualServerRouteSpec(&virtualServerRoute.Spec, field.NewPath("spec"), "", "/") + return allErrs.ToAggregate() +} + +// ValidateVirtualServerRouteForVirtualServer validates a VirtualServerRoute for a VirtualServer represented by its host and path prefix. +func ValidateVirtualServerRouteForVirtualServer(virtualServerRoute *v1alpha1.VirtualServerRoute, virtualServerHost string, pathPrefix string) error { + allErrs := validateVirtualServerRouteSpec(&virtualServerRoute.Spec, field.NewPath("spec"), virtualServerHost, pathPrefix) + return allErrs.ToAggregate() +} + +func validateVirtualServerRouteSpec(spec *v1alpha1.VirtualServerRouteSpec, fieldPath *field.Path, virtualServerHost string, pathPrefix string) field.ErrorList { + allErrs := field.ErrorList{} + + allErrs = append(allErrs, validateVirtualServerRouteHost(spec.Host, virtualServerHost, fieldPath.Child("host"))...) + + upstreamErrs, upstreamNames := validateUpstreams(spec.Upstreams, fieldPath.Child("upstreams")) + allErrs = append(allErrs, upstreamErrs...) + + allErrs = append(allErrs, validateVirtualServerRouteSubroutes(spec.Subroutes, fieldPath.Child("subroutes"), upstreamNames, pathPrefix)...) + + return allErrs +} + +func validateVirtualServerRouteHost(host string, virtualServerHost string, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + allErrs = append(allErrs, validateHost(host, fieldPath)...) + + if virtualServerHost != "" && host != virtualServerHost { + msg := fmt.Sprintf("must be equal to '%s'", virtualServerHost) + allErrs = append(allErrs, field.Invalid(fieldPath, host, msg)) + } + + return allErrs +} + +func validateVirtualServerRouteSubroutes(routes []v1alpha1.Route, fieldPath *field.Path, upstreamNames sets.String, pathPrefix string) field.ErrorList { + allErrs := field.ErrorList{} + + allPaths := sets.String{} + + for i, r := range routes { + idxPath := fieldPath.Index(i) + + isRouteFieldForbidden := true + routeErrs := validateRoute(r, idxPath, upstreamNames, isRouteFieldForbidden) + + if pathPrefix != "" && !strings.HasPrefix(r.Path, pathPrefix) { + msg := fmt.Sprintf("must start with '%s'", pathPrefix) + routeErrs = append(routeErrs, field.Invalid(idxPath, r.Path, msg)) + } + + if len(routeErrs) > 0 { + allErrs = append(allErrs, routeErrs...) + } else if allPaths.Has(r.Path) { + allErrs = append(allErrs, field.Duplicate(idxPath.Child("path"), r.Path)) + } else { + allPaths.Insert(r.Path) + } + } + + return allErrs +} diff --git a/pkg/apis/configuration/validation/validation_test.go b/pkg/apis/configuration/validation/validation_test.go new file mode 100644 index 0000000000..bacb1c46da --- /dev/null +++ b/pkg/apis/configuration/validation/validation_test.go @@ -0,0 +1,1380 @@ +package validation + +import ( + "testing" + + "k8s.io/apimachinery/pkg/util/sets" + + "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +func TestValidateVirtualServer(t *testing.T) { + virtualServer := v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + Spec: v1alpha1.VirtualServerSpec{ + Host: "example.com", + TLS: &v1alpha1.TLS{ + Secret: "abc", + }, + Upstreams: []v1alpha1.Upstream{ + { + Name: "first", + Service: "service-1", + Port: 80, + }, + { + Name: "second", + Service: "service-2", + Port: 80, + }, + }, + Routes: []v1alpha1.Route{ + { + Path: "/first", + Upstream: "first", + }, + { + Path: "/second", + Upstream: "second", + }, + }, + }, + } + + err := ValidateVirtualServer(&virtualServer) + if err != nil { + t.Errorf("ValidateVirtualServer() returned error %v for valid input %v", err, virtualServer) + } +} + +func TestValidateHost(t *testing.T) { + validHosts := []string{ + "hello", + "example.com", + "hello-world-1", + } + + for _, h := range validHosts { + allErrs := validateHost(h, field.NewPath("host")) + if len(allErrs) > 0 { + t.Errorf("validateHost(%q) returned errors %v for valid input", h, allErrs) + } + } + + invalidHosts := []string{ + "", + "*", + "..", + ".example.com", + "-hello-world-1", + } + + for _, h := range invalidHosts { + allErrs := validateHost(h, field.NewPath("host")) + if len(allErrs) == 0 { + t.Errorf("validateHost(%q) returned no errors for invalid input", h) + } + } +} + +func TestValidateTLS(t *testing.T) { + validTLSes := []*v1alpha1.TLS{ + nil, + { + Secret: "my-secret", + }, + } + + for _, tls := range validTLSes { + allErrs := validateTLS(tls, field.NewPath("tls")) + if len(allErrs) > 0 { + t.Errorf("validateTLS() returned errors %v for valid input %v", allErrs, tls) + } + } + + invalidTLSes := []*v1alpha1.TLS{ + { + Secret: "", + }, + { + Secret: "-", + }, + { + Secret: "a/b", + }, + } + + for _, tls := range invalidTLSes { + allErrs := validateTLS(tls, field.NewPath("tls")) + if len(allErrs) == 0 { + t.Errorf("validateTLS() returned no errors for invalid input %v", tls) + } + } +} + +func TestValidateUpstreams(t *testing.T) { + tests := []struct { + upstreams []v1alpha1.Upstream + expectedUpstreamNames sets.String + msg string + }{ + { + upstreams: []v1alpha1.Upstream{}, + expectedUpstreamNames: sets.String{}, + msg: "no upstreams", + }, + { + upstreams: []v1alpha1.Upstream{ + { + Name: "upstream1", + Service: "test-1", + Port: 80, + }, + { + Name: "upstream2", + Service: "test-2", + Port: 80, + }, + }, + expectedUpstreamNames: map[string]sets.Empty{ + "upstream1": sets.Empty{}, + "upstream2": sets.Empty{}, + }, + msg: "2 valid upstreams", + }, + } + + for _, test := range tests { + allErrs, resultUpstreamNames := validateUpstreams(test.upstreams, field.NewPath("upstreams")) + if len(allErrs) > 0 { + t.Errorf("validateUpstreams() returned errors %v for valid input for the case of %s", allErrs, test.msg) + } + if !resultUpstreamNames.Equal(test.expectedUpstreamNames) { + t.Errorf("validateUpstreams() returned %v expected %v for the case of %s", resultUpstreamNames, test.expectedUpstreamNames, test.msg) + } + } +} + +func TestValidateUpstreamsFails(t *testing.T) { + tests := []struct { + upstreams []v1alpha1.Upstream + expectedUpstreamNames sets.String + msg string + }{ + { + upstreams: []v1alpha1.Upstream{ + { + Name: "@upstream1", + Service: "test-1", + Port: 80, + }, + }, + expectedUpstreamNames: sets.String{}, + msg: "invalid upstream name", + }, + { + upstreams: []v1alpha1.Upstream{ + { + Name: "upstream1", + Service: "@test-1", + Port: 80, + }, + }, + expectedUpstreamNames: map[string]sets.Empty{ + "upstream1": sets.Empty{}, + }, + msg: "invalid service", + }, + { + upstreams: []v1alpha1.Upstream{ + { + Name: "upstream1", + Service: "test-1", + Port: 0, + }, + }, + expectedUpstreamNames: map[string]sets.Empty{ + "upstream1": sets.Empty{}, + }, + msg: "invalid port", + }, + { + upstreams: []v1alpha1.Upstream{ + { + Name: "upstream1", + Service: "test-1", + Port: 80, + }, + { + Name: "upstream1", + Service: "test-2", + Port: 80, + }, + }, + expectedUpstreamNames: map[string]sets.Empty{ + "upstream1": sets.Empty{}, + }, + msg: "duplicated upstreams", + }, + } + + for _, test := range tests { + allErrs, resultUpstreamNames := validateUpstreams(test.upstreams, field.NewPath("upstreams")) + if len(allErrs) == 0 { + t.Errorf("validateUpstreams() returned no errors for the case of %s", test.msg) + } + if !resultUpstreamNames.Equal(test.expectedUpstreamNames) { + t.Errorf("validateUpstreams() returned %v expected %v for the case of %s", resultUpstreamNames, test.expectedUpstreamNames, test.msg) + } + } +} + +func TestValidateDNS1035Label(t *testing.T) { + validNames := []string{ + "test", + "test-123", + } + + for _, name := range validNames { + allErrs := validateDNS1035Label(name, field.NewPath("name")) + if len(allErrs) > 0 { + t.Errorf("validateDNS1035Label(%q) returned errors %v for valid input", name, allErrs) + } + } + + invalidNames := []string{ + "", + "123", + "test.123", + } + + for _, name := range invalidNames { + allErrs := validateDNS1035Label(name, field.NewPath("name")) + if len(allErrs) == 0 { + t.Errorf("validateDNS1035Label(%q) returned no errors for invalid input", name) + } + } +} + +func TestValidateVirtualServerRoutes(t *testing.T) { + tests := []struct { + routes []v1alpha1.Route + upstreamNames sets.String + msg string + }{ + { + routes: []v1alpha1.Route{}, + upstreamNames: sets.String{}, + msg: "no routes", + }, + { + routes: []v1alpha1.Route{ + { + Path: "/", + Upstream: "test", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test": sets.Empty{}, + }, + msg: "valid route", + }, + } + + for _, test := range tests { + allErrs := validateVirtualServerRoutes(test.routes, field.NewPath("routes"), test.upstreamNames) + if len(allErrs) > 0 { + t.Errorf("validateVirtualServerRoutes() returned errors %v for valid input for the case of %s", allErrs, test.msg) + } + } +} + +func TestValidateVirtualServerRoutesFails(t *testing.T) { + tests := []struct { + routes []v1alpha1.Route + upstreamNames sets.String + msg string + }{ + { + routes: []v1alpha1.Route{ + { + Path: "/test", + Upstream: "test-1", + }, + { + Path: "/test", + Upstream: "test-2", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + msg: "duplicated paths", + }, + + { + routes: []v1alpha1.Route{ + { + Path: "", + Upstream: "", + }, + }, + upstreamNames: map[string]sets.Empty{}, + msg: "invalid route", + }, + } + + for _, test := range tests { + allErrs := validateVirtualServerRoutes(test.routes, field.NewPath("routes"), test.upstreamNames) + if len(allErrs) == 0 { + t.Errorf("validateVirtualServerRoutes() returned no errors for the case of %s", test.msg) + } + } +} + +func TestValidateRoute(t *testing.T) { + tests := []struct { + route v1alpha1.Route + upstreamNames sets.String + isRouteFieldForbidden bool + msg string + }{ + { + route: v1alpha1.Route{ + + Path: "/", + Upstream: "test", + }, + upstreamNames: map[string]sets.Empty{ + "test": sets.Empty{}, + }, + isRouteFieldForbidden: false, + msg: "valid route with upstream", + }, + { + route: v1alpha1.Route{ + Path: "/", + Splits: []v1alpha1.Split{ + { + Weight: 90, + Upstream: "test-1", + }, + { + Weight: 10, + Upstream: "test-2", + }, + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + isRouteFieldForbidden: false, + msg: "valid upstream with splits", + }, + { + route: v1alpha1.Route{ + Path: "/", + Rules: &v1alpha1.Rules{ + Conditions: []v1alpha1.Condition{ + { + Header: "x-version", + }, + }, + Matches: []v1alpha1.Match{ + { + Values: []string{ + "test-1", + }, + Upstream: "test-1", + }, + }, + DefaultUpstream: "test-2", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + isRouteFieldForbidden: false, + msg: "valid upstream with rules", + }, + { + route: v1alpha1.Route{ + + Path: "/", + Route: "default/test", + }, + upstreamNames: map[string]sets.Empty{}, + isRouteFieldForbidden: false, + msg: "valid route with route", + }, + } + + for _, test := range tests { + allErrs := validateRoute(test.route, field.NewPath("route"), test.upstreamNames, test.isRouteFieldForbidden) + if len(allErrs) > 0 { + t.Errorf("validateRoute() returned errors %v for valid input for the case of %s", allErrs, test.msg) + } + } +} + +func TestValidateRouteFails(t *testing.T) { + tests := []struct { + route v1alpha1.Route + upstreamNames sets.String + isRouteFieldForbidden bool + msg string + }{ + { + route: v1alpha1.Route{ + Path: "", + Upstream: "test", + }, + upstreamNames: map[string]sets.Empty{ + "test": sets.Empty{}, + }, + isRouteFieldForbidden: false, + msg: "empty path", + }, + { + route: v1alpha1.Route{ + Path: "/test", + Upstream: "-test", + }, + upstreamNames: sets.String{}, + isRouteFieldForbidden: false, + msg: "invalid upstream", + }, + { + route: v1alpha1.Route{ + Path: "/", + Upstream: "test", + }, + upstreamNames: sets.String{}, + isRouteFieldForbidden: false, + msg: "non-existing upstream", + }, + { + route: v1alpha1.Route{ + Path: "/", + Upstream: "test", + Splits: []v1alpha1.Split{ + { + Weight: 90, + Upstream: "test-1", + }, + { + Weight: 10, + Upstream: "test-2", + }, + }, + }, + upstreamNames: map[string]sets.Empty{ + "test": sets.Empty{}, + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + isRouteFieldForbidden: false, + msg: "both upstream and splits exist", + }, + { + route: v1alpha1.Route{ + Path: "/", + Upstream: "test", + Rules: &v1alpha1.Rules{ + Conditions: []v1alpha1.Condition{ + { + Header: "x-version", + }, + }, + Matches: []v1alpha1.Match{ + { + Values: []string{ + "test-1", + }, + Upstream: "test-1", + }, + }, + DefaultUpstream: "test-2", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test": sets.Empty{}, + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + isRouteFieldForbidden: false, + msg: "both upstream and rules exist", + }, + { + route: v1alpha1.Route{ + Path: "/", + Splits: []v1alpha1.Split{ + { + Weight: 90, + Upstream: "test-1", + }, + { + Weight: 10, + Upstream: "test-2", + }, + }, + Rules: &v1alpha1.Rules{ + Conditions: []v1alpha1.Condition{ + { + Header: "x-version", + }, + }, + Matches: []v1alpha1.Match{ + { + Values: []string{ + "test-1", + }, + Upstream: "test-1", + }, + }, + DefaultUpstream: "test-2", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + isRouteFieldForbidden: false, + msg: "both splits and rules exist", + }, + { + route: v1alpha1.Route{ + Path: "/", + Route: "default/test", + }, + upstreamNames: map[string]sets.Empty{}, + isRouteFieldForbidden: true, + msg: "route field exists but is forbidden", + }, + } + + for _, test := range tests { + allErrs := validateRoute(test.route, field.NewPath("route"), test.upstreamNames, test.isRouteFieldForbidden) + if len(allErrs) == 0 { + t.Errorf("validateRoute() returned no errors for invalid input for the case of %s", test.msg) + } + } +} + +func TestValidateRouteField(t *testing.T) { + validRouteFields := []string{ + "coffee", + "default/coffee", + } + + for _, rf := range validRouteFields { + allErrs := validateRouteField(rf, field.NewPath("route")) + if len(allErrs) > 0 { + t.Errorf("validRouteField(%q) returned errors %v for valid input", rf, allErrs) + } + } + + invalidRouteFields := []string{ + "-", + "/coffee", + "-/coffee", + } + + for _, rf := range invalidRouteFields { + allErrs := validateRouteField(rf, field.NewPath("route")) + if len(allErrs) == 0 { + t.Errorf("validRouteField(%q) returned no errors for invalid input", rf) + } + } +} + +func TestValdateReferencedUpstream(t *testing.T) { + upstream := "test" + upstreamNames := map[string]sets.Empty{ + "test": sets.Empty{}, + } + + allErrs := validateReferencedUpstream(upstream, field.NewPath("upstream"), upstreamNames) + if len(allErrs) > 0 { + t.Errorf("validateReferencedUpstream() returned errors %v for valid input", allErrs) + } +} + +func TestValdateUpstreamFails(t *testing.T) { + tests := []struct { + upstream string + upstreamNames sets.String + msg string + }{ + { + upstream: "", + upstreamNames: map[string]sets.Empty{}, + msg: "empty upstream", + }, + { + upstream: "-test", + upstreamNames: map[string]sets.Empty{}, + msg: "invalid upstream", + }, + { + upstream: "test", + upstreamNames: map[string]sets.Empty{}, + msg: "non-existing upstream", + }, + } + + for _, test := range tests { + allErrs := validateReferencedUpstream(test.upstream, field.NewPath("upstream"), test.upstreamNames) + if len(allErrs) == 0 { + t.Errorf("validateReferencedUpstream() returned no errors for invalid input for the case of %s", test.msg) + } + } +} + +func TestValidatePath(t *testing.T) { + validPaths := []string{ + "/", + "/path", + "/a-1/_A/", + } + + for _, path := range validPaths { + allErrs := validatePath(path, field.NewPath("path")) + if len(allErrs) > 0 { + t.Errorf("validatePath(%q) returned errors %v for valid input", path, allErrs) + } + } + + invalidPaths := []string{ + "", + " /", + "/ ", + "/{", + "/}", + "/abc;", + } + + for _, path := range invalidPaths { + allErrs := validatePath(path, field.NewPath("path")) + if len(allErrs) == 0 { + t.Errorf("validatePath(%q) returned no errors for invalid input", path) + } + } +} + +func TestValidateSplits(t *testing.T) { + splits := []v1alpha1.Split{ + { + Weight: 90, + Upstream: "test-1", + }, + { + Weight: 10, + Upstream: "test-2", + }, + } + upstreamNames := map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + } + + allErrs := validateSplits(splits, field.NewPath("splits"), upstreamNames) + if len(allErrs) > 0 { + t.Errorf("validateSplits() returned errors %v for valid input", allErrs) + } +} + +func TestValidateSplitsFails(t *testing.T) { + tests := []struct { + splits []v1alpha1.Split + upstreamNames sets.String + msg string + }{ + { + splits: []v1alpha1.Split{ + { + Weight: 90, + Upstream: "test-1", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + }, + msg: "only one split", + }, + { + splits: []v1alpha1.Split{ + { + Weight: 123, + Upstream: "test-1", + }, + { + Weight: 10, + Upstream: "test-2", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + msg: "invalid weight", + }, + { + splits: []v1alpha1.Split{ + { + Weight: 99, + Upstream: "test-1", + }, + { + Weight: 99, + Upstream: "test-2", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + msg: "invalid total weight", + }, + { + splits: []v1alpha1.Split{ + { + Weight: 90, + Upstream: "", + }, + { + Weight: 10, + Upstream: "test-2", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + msg: "invalid upstream", + }, + { + splits: []v1alpha1.Split{ + { + Weight: 90, + Upstream: "some-upstream", + }, + { + Weight: 10, + Upstream: "test-2", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + msg: "non-existing upstream", + }, + } + + for _, test := range tests { + allErrs := validateSplits(test.splits, field.NewPath("splits"), test.upstreamNames) + if len(allErrs) == 0 { + t.Errorf("validateSplits() returned no errors for invalid input for the case of %s", test.msg) + } + } +} + +func TestValidateRules(t *testing.T) { + rules := v1alpha1.Rules{ + Conditions: []v1alpha1.Condition{ + { + Header: "x-version", + }, + }, + Matches: []v1alpha1.Match{ + { + Values: []string{ + "test-1", + }, + Upstream: "test-1", + }, + }, + DefaultUpstream: "test-2", + } + + upstreamNames := map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + } + + allErrs := validateRules(&rules, field.NewPath("rules"), upstreamNames) + if len(allErrs) > 0 { + t.Errorf("validateRules() returned errors %v for valid input", allErrs) + } +} + +func TestValidateRulesFails(t *testing.T) { + tests := []struct { + rules v1alpha1.Rules + upstreamNames sets.String + msg string + }{ + { + rules: v1alpha1.Rules{ + Conditions: []v1alpha1.Condition{}, + Matches: []v1alpha1.Match{ + { + Values: []string{ + "test-1", + }, + Upstream: "test-1", + }, + }, + DefaultUpstream: "test-2", + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + msg: "no conditions", + }, + { + rules: v1alpha1.Rules{ + Conditions: []v1alpha1.Condition{ + { + Header: "x-version", + }, + }, + Matches: []v1alpha1.Match{}, + DefaultUpstream: "test-2", + }, + upstreamNames: map[string]sets.Empty{ + "test-2": sets.Empty{}, + }, + msg: "no matches", + }, + { + rules: v1alpha1.Rules{ + Conditions: []v1alpha1.Condition{ + { + Header: "x-version", + }, + }, + Matches: []v1alpha1.Match{ + { + Values: []string{ + "test-1", + }, + Upstream: "test-1", + }, + }, + DefaultUpstream: "", + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + }, + msg: "no default upstream", + }, + { + rules: v1alpha1.Rules{ + Conditions: []v1alpha1.Condition{ + { + Header: "x-version", + Cookie: "user", + }, + }, + Matches: []v1alpha1.Match{ + { + Values: []string{ + "test-1", + }, + Upstream: "test-1", + }, + }, + DefaultUpstream: "test", + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test": sets.Empty{}, + }, + msg: "invalid values in a match", + }, + } + + for _, test := range tests { + allErrs := validateRules(&test.rules, field.NewPath("rules"), test.upstreamNames) + if len(allErrs) == 0 { + t.Errorf("validateRules() returned no errors for invalid input for the case of %s", test.msg) + } + } +} + +func TestValidateCondition(t *testing.T) { + tests := []struct { + condition v1alpha1.Condition + msg string + }{ + { + condition: v1alpha1.Condition{ + Header: "x-version", + }, + msg: "valid header", + }, + { + condition: v1alpha1.Condition{ + Cookie: "my_cookie", + }, + msg: "valid cookie", + }, + { + condition: v1alpha1.Condition{ + Argument: "arg", + }, + msg: "valid argument", + }, + { + condition: v1alpha1.Condition{ + Variable: "$request_method", + }, + msg: "valid variable", + }, + } + + for _, test := range tests { + allErrs := validateCondition(test.condition, field.NewPath("condition")) + if len(allErrs) > 0 { + t.Errorf("validateCondition() returned errors %v for valid input for the case of %s", allErrs, test.msg) + } + } +} + +func TestValidateConditionFails(t *testing.T) { + tests := []struct { + condition v1alpha1.Condition + msg string + }{ + { + condition: v1alpha1.Condition{}, + msg: "empty condition", + }, + { + condition: v1alpha1.Condition{ + Header: "x-version", + Cookie: "user", + Argument: "answer", + Variable: "$request_method", + }, + msg: "invalid condition", + }, + { + condition: v1alpha1.Condition{ + Header: "x_version", + }, + msg: "invalid header", + }, + { + condition: v1alpha1.Condition{ + Cookie: "my-cookie", + }, + msg: "invalid cookie", + }, + { + condition: v1alpha1.Condition{ + Argument: "my-arg", + }, + msg: "invalid argument", + }, + { + condition: v1alpha1.Condition{ + Variable: "request_method", + }, + msg: "invalid variable", + }, + } + + for _, test := range tests { + allErrs := validateCondition(test.condition, field.NewPath("condition")) + if len(allErrs) == 0 { + t.Errorf("validateCondition() returned no errors for invalid input for the case of %s", test.msg) + } + } +} + +func TestIsCookieName(t *testing.T) { + validCookieNames := []string{ + "123", + "my_cookie", + } + + for _, name := range validCookieNames { + errs := isCookieName(name) + if len(errs) > 0 { + t.Errorf("isCookieName(%q) returned errors %v for valid input", name, errs) + } + } + + invalidCookieNames := []string{ + "", + "my-cookie", + "cookie!", + } + + for _, name := range invalidCookieNames { + errs := isCookieName(name) + if len(errs) == 0 { + t.Errorf("isCookieName(%q) returned no errors for invalid input", name) + } + } +} + +func TestIsArgumentName(t *testing.T) { + validArgumentNames := []string{ + "123", + "my_arg", + } + + for _, name := range validArgumentNames { + errs := isArgumentName(name) + if len(errs) > 0 { + t.Errorf("isArgumentName(%q) returned errors %v for valid input", name, errs) + } + } + + invalidArgumentNames := []string{ + "", + "my-arg", + "arg!", + } + + for _, name := range invalidArgumentNames { + errs := isArgumentName(name) + if len(errs) == 0 { + t.Errorf("isArgumentName(%q) returned no errors for invalid input", name) + } + } +} + +func TestValidateVariableName(t *testing.T) { + validNames := []string{ + "$request_method", + } + + for _, name := range validNames { + allErrs := validateVariableName(name, field.NewPath("variable")) + if len(allErrs) > 0 { + t.Errorf("validateVariableName(%q) returned errors %v for valid input", name, allErrs) + } + } + + invalidNames := []string{ + "request_method", + "$request_id", + } + + for _, name := range invalidNames { + allErrs := validateVariableName(name, field.NewPath("variable")) + if len(allErrs) == 0 { + t.Errorf("validateVariableName(%q) returned no errors for invalid input", name) + } + } +} + +func TestValidateMatch(t *testing.T) { + match := v1alpha1.Match{ + Values: []string{ + "value1", + "value2", + }, + Upstream: "test", + } + conditionsCount := 2 + upstreamNames := map[string]sets.Empty{ + "test": sets.Empty{}, + } + + allErrs := validateMatch(match, field.NewPath("match"), conditionsCount, upstreamNames) + if len(allErrs) > 0 { + t.Errorf("validateMatch() returned errors %v for valid input", allErrs) + } +} + +func TestValidateMatchFails(t *testing.T) { + tests := []struct { + match v1alpha1.Match + conditionsCount int + upstreamNames sets.String + msg string + }{ + { + match: v1alpha1.Match{ + Values: []string{}, + Upstream: "test", + }, + conditionsCount: 1, + upstreamNames: map[string]sets.Empty{ + "test": sets.Empty{}, + }, + msg: "invalid number of values", + }, + { + match: v1alpha1.Match{ + Values: []string{ + `abc"`, + }, + Upstream: "test", + }, + conditionsCount: 1, + upstreamNames: map[string]sets.Empty{ + "test": sets.Empty{}, + }, + msg: "invalid value", + }, + { + match: v1alpha1.Match{ + Values: []string{ + "value", + }, + Upstream: "-invalid", + }, + conditionsCount: 1, + upstreamNames: map[string]sets.Empty{}, + msg: "invalid upstream", + }, + } + + for _, test := range tests { + allErrs := validateMatch(test.match, field.NewPath("match"), test.conditionsCount, test.upstreamNames) + if len(allErrs) == 0 { + t.Errorf("validateMatch() returned no errors for invalid input for the case of %s", test.msg) + } + } +} + +func TestIsValidMatchValue(t *testing.T) { + validValues := []string{ + "abc", + "123", + `\" + abc\"`, + `\"`, + } + + for _, value := range validValues { + errs := isValidMatchValue(value) + if len(errs) > 0 { + t.Errorf("isValidMatchValue(%q) returned errors %v for valid input", value, errs) + } + } + + invalidValues := []string{ + `"`, + `\`, + `abc"`, + `abc\\\`, + `a"b`, + } + + for _, value := range invalidValues { + errs := isValidMatchValue(value) + if len(errs) == 0 { + t.Errorf("isValidMatchValue(%q) returned no errors for invalid input", value) + } + } +} + +func TestValidateVirtualServerRoute(t *testing.T) { + virtualServerRoute := v1alpha1.VirtualServerRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee", + Namespace: "default", + }, + Spec: v1alpha1.VirtualServerRouteSpec{ + Host: "example.com", + Upstreams: []v1alpha1.Upstream{ + { + Name: "first", + Service: "service-1", + Port: 80, + }, + { + Name: "second", + Service: "service-2", + Port: 80, + }, + }, + Subroutes: []v1alpha1.Route{ + { + Path: "/test/first", + Upstream: "first", + }, + { + Path: "/test/second", + Upstream: "second", + }, + }, + }, + } + + err := ValidateVirtualServerRoute(&virtualServerRoute) + if err != nil { + t.Errorf("ValidateVirtualServerRoute() returned error %v for valid input %v", err, virtualServerRoute) + } +} + +func TestValidateVirtualServerRouteForVirtualServer(t *testing.T) { + virtualServerRoute := v1alpha1.VirtualServerRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee", + Namespace: "default", + }, + Spec: v1alpha1.VirtualServerRouteSpec{ + Host: "example.com", + Upstreams: []v1alpha1.Upstream{ + { + Name: "first", + Service: "service-1", + Port: 80, + }, + { + Name: "second", + Service: "service-2", + Port: 80, + }, + }, + Subroutes: []v1alpha1.Route{ + { + Path: "/test/first", + Upstream: "first", + }, + { + Path: "/test/second", + Upstream: "second", + }, + }, + }, + } + virtualServerHost := "example.com" + pathPrefix := "/test" + + err := ValidateVirtualServerRouteForVirtualServer(&virtualServerRoute, virtualServerHost, pathPrefix) + if err != nil { + t.Errorf("ValidateVirtualServerRouteForVirtualServer() returned error %v for valid input %v", err, virtualServerRoute) + } +} + +func TestValidateVirtualServerRouteHost(t *testing.T) { + virtualServerHost := "example.com" + + validHost := "example.com" + + allErrs := validateVirtualServerRouteHost(validHost, virtualServerHost, field.NewPath("host")) + if len(allErrs) > 0 { + t.Errorf("validateVirtualServerRouteHost() returned errors %v for valid input", allErrs) + } + + invalidHost := "foo.example.com" + + allErrs = validateVirtualServerRouteHost(invalidHost, virtualServerHost, field.NewPath("host")) + if len(allErrs) == 0 { + t.Errorf("validateVirtualServerRouteHost() returned no errors for invalid input") + } +} + +func TestValidateVirtualServerRouteSubroutes(t *testing.T) { + tests := []struct { + routes []v1alpha1.Route + upstreamNames sets.String + pathPrefix string + msg string + }{ + { + routes: []v1alpha1.Route{}, + upstreamNames: sets.String{}, + pathPrefix: "/", + msg: "no routes", + }, + { + routes: []v1alpha1.Route{ + { + Path: "/", + Upstream: "test", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test": sets.Empty{}, + }, + pathPrefix: "/", + msg: "valid route", + }, + } + + for _, test := range tests { + allErrs := validateVirtualServerRouteSubroutes(test.routes, field.NewPath("subroutes"), test.upstreamNames, test.pathPrefix) + if len(allErrs) > 0 { + t.Errorf("validateVirtualServerRouteSubroutes() returned errors %v for valid input for the case of %s", allErrs, test.msg) + } + } +} + +func TestValidateVirtualServerRouteSubroutesFails(t *testing.T) { + tests := []struct { + routes []v1alpha1.Route + upstreamNames sets.String + pathPrefix string + msg string + }{ + { + routes: []v1alpha1.Route{ + { + Path: "/test", + Upstream: "test-1", + }, + { + Path: "/test", + Upstream: "test-2", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + "test-2": sets.Empty{}, + }, + pathPrefix: "/", + msg: "duplicated paths", + }, + { + routes: []v1alpha1.Route{ + { + Path: "", + Upstream: "", + }, + }, + upstreamNames: map[string]sets.Empty{}, + pathPrefix: "", + msg: "invalid route", + }, + { + routes: []v1alpha1.Route{ + { + Path: "/", + Upstream: "test-1", + }, + }, + upstreamNames: map[string]sets.Empty{ + "test-1": sets.Empty{}, + }, + pathPrefix: "/abc", + msg: "invalid prefix", + }, + } + + for _, test := range tests { + allErrs := validateVirtualServerRouteSubroutes(test.routes, field.NewPath("subroutes"), test.upstreamNames, test.pathPrefix) + if len(allErrs) == 0 { + t.Errorf("validateVirtualServerRouteSubroutes() returned no errors for the case of %s", test.msg) + } + } +} diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go new file mode 100644 index 0000000000..0e6d9a7fba --- /dev/null +++ b/pkg/client/clientset/versioned/clientset.go @@ -0,0 +1,82 @@ +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + k8sv1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned/typed/configuration/v1alpha1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + K8sV1alpha1() k8sv1alpha1.K8sV1alpha1Interface + // Deprecated: please explicitly pick a version if possible. + K8s() k8sv1alpha1.K8sV1alpha1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + k8sV1alpha1 *k8sv1alpha1.K8sV1alpha1Client +} + +// K8sV1alpha1 retrieves the K8sV1alpha1Client +func (c *Clientset) K8sV1alpha1() k8sv1alpha1.K8sV1alpha1Interface { + return c.k8sV1alpha1 +} + +// Deprecated: K8s retrieves the default version of K8sClient. +// Please explicitly pick a version. +func (c *Clientset) K8s() k8sv1alpha1.K8sV1alpha1Interface { + return c.k8sV1alpha1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.k8sV1alpha1, err = k8sv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.k8sV1alpha1 = k8sv1alpha1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.k8sV1alpha1 = k8sv1alpha1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/pkg/client/clientset/versioned/doc.go b/pkg/client/clientset/versioned/doc.go new file mode 100644 index 0000000000..0e0c2a8900 --- /dev/null +++ b/pkg/client/clientset/versioned/doc.go @@ -0,0 +1,4 @@ +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 0000000000..25b9e1b6dd --- /dev/null +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,66 @@ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + clientset "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned" + k8sv1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned/typed/configuration/v1alpha1" + fakek8sv1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +var _ clientset.Interface = &Clientset{} + +// K8sV1alpha1 retrieves the K8sV1alpha1Client +func (c *Clientset) K8sV1alpha1() k8sv1alpha1.K8sV1alpha1Interface { + return &fakek8sv1alpha1.FakeK8sV1alpha1{Fake: &c.Fake} +} + +// K8s retrieves the K8sV1alpha1Client +func (c *Clientset) K8s() k8sv1alpha1.K8sV1alpha1Interface { + return &fakek8sv1alpha1.FakeK8sV1alpha1{Fake: &c.Fake} +} diff --git a/pkg/client/clientset/versioned/fake/doc.go b/pkg/client/clientset/versioned/fake/doc.go new file mode 100644 index 0000000000..3630ed1cd1 --- /dev/null +++ b/pkg/client/clientset/versioned/fake/doc.go @@ -0,0 +1,4 @@ +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go new file mode 100644 index 0000000000..069b7079e4 --- /dev/null +++ b/pkg/client/clientset/versioned/fake/register.go @@ -0,0 +1,40 @@ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + k8sv1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) +var parameterCodec = runtime.NewParameterCodec(scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + k8sv1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/pkg/client/clientset/versioned/scheme/doc.go b/pkg/client/clientset/versioned/scheme/doc.go new file mode 100644 index 0000000000..14db57a58f --- /dev/null +++ b/pkg/client/clientset/versioned/scheme/doc.go @@ -0,0 +1,4 @@ +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go new file mode 100644 index 0000000000..c41bf56d6d --- /dev/null +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -0,0 +1,40 @@ +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + k8sv1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + k8sv1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/configuration_client.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/configuration_client.go new file mode 100644 index 0000000000..2271e64902 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/configuration_client.go @@ -0,0 +1,79 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned/scheme" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + rest "k8s.io/client-go/rest" +) + +type K8sV1alpha1Interface interface { + RESTClient() rest.Interface + VirtualServersGetter + VirtualServerRoutesGetter +} + +// K8sV1alpha1Client is used to interact with features provided by the k8s.nginx.org group. +type K8sV1alpha1Client struct { + restClient rest.Interface +} + +func (c *K8sV1alpha1Client) VirtualServers(namespace string) VirtualServerInterface { + return newVirtualServers(c, namespace) +} + +func (c *K8sV1alpha1Client) VirtualServerRoutes(namespace string) VirtualServerRouteInterface { + return newVirtualServerRoutes(c, namespace) +} + +// NewForConfig creates a new K8sV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*K8sV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &K8sV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new K8sV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *K8sV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new K8sV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *K8sV1alpha1Client { + return &K8sV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *K8sV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/doc.go new file mode 100644 index 0000000000..93a7ca4e0e --- /dev/null +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/doc.go @@ -0,0 +1,4 @@ +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/doc.go new file mode 100644 index 0000000000..2b5ba4c8e4 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/doc.go @@ -0,0 +1,4 @@ +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_configuration_client.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_configuration_client.go new file mode 100644 index 0000000000..944cbcd607 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_configuration_client.go @@ -0,0 +1,28 @@ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned/typed/configuration/v1alpha1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeK8sV1alpha1 struct { + *testing.Fake +} + +func (c *FakeK8sV1alpha1) VirtualServers(namespace string) v1alpha1.VirtualServerInterface { + return &FakeVirtualServers{c, namespace} +} + +func (c *FakeK8sV1alpha1) VirtualServerRoutes(namespace string) v1alpha1.VirtualServerRouteInterface { + return &FakeVirtualServerRoutes{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeK8sV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_virtualserver.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_virtualserver.go new file mode 100644 index 0000000000..dee0a33e58 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_virtualserver.go @@ -0,0 +1,112 @@ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeVirtualServers implements VirtualServerInterface +type FakeVirtualServers struct { + Fake *FakeK8sV1alpha1 + ns string +} + +var virtualserversResource = schema.GroupVersionResource{Group: "k8s.nginx.org", Version: "v1alpha1", Resource: "virtualservers"} + +var virtualserversKind = schema.GroupVersionKind{Group: "k8s.nginx.org", Version: "v1alpha1", Kind: "VirtualServer"} + +// Get takes name of the virtualServer, and returns the corresponding virtualServer object, and an error if there is any. +func (c *FakeVirtualServers) Get(name string, options v1.GetOptions) (result *v1alpha1.VirtualServer, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(virtualserversResource, c.ns, name), &v1alpha1.VirtualServer{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.VirtualServer), err +} + +// List takes label and field selectors, and returns the list of VirtualServers that match those selectors. +func (c *FakeVirtualServers) List(opts v1.ListOptions) (result *v1alpha1.VirtualServerList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(virtualserversResource, virtualserversKind, c.ns, opts), &v1alpha1.VirtualServerList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.VirtualServerList{ListMeta: obj.(*v1alpha1.VirtualServerList).ListMeta} + for _, item := range obj.(*v1alpha1.VirtualServerList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested virtualServers. +func (c *FakeVirtualServers) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(virtualserversResource, c.ns, opts)) + +} + +// Create takes the representation of a virtualServer and creates it. Returns the server's representation of the virtualServer, and an error, if there is any. +func (c *FakeVirtualServers) Create(virtualServer *v1alpha1.VirtualServer) (result *v1alpha1.VirtualServer, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(virtualserversResource, c.ns, virtualServer), &v1alpha1.VirtualServer{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.VirtualServer), err +} + +// Update takes the representation of a virtualServer and updates it. Returns the server's representation of the virtualServer, and an error, if there is any. +func (c *FakeVirtualServers) Update(virtualServer *v1alpha1.VirtualServer) (result *v1alpha1.VirtualServer, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(virtualserversResource, c.ns, virtualServer), &v1alpha1.VirtualServer{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.VirtualServer), err +} + +// Delete takes name of the virtualServer and deletes it. Returns an error if one occurs. +func (c *FakeVirtualServers) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(virtualserversResource, c.ns, name), &v1alpha1.VirtualServer{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeVirtualServers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(virtualserversResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.VirtualServerList{}) + return err +} + +// Patch applies the patch and returns the patched virtualServer. +func (c *FakeVirtualServers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.VirtualServer, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(virtualserversResource, c.ns, name, data, subresources...), &v1alpha1.VirtualServer{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.VirtualServer), err +} diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_virtualserverroute.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_virtualserverroute.go new file mode 100644 index 0000000000..20083301fe --- /dev/null +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_virtualserverroute.go @@ -0,0 +1,112 @@ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeVirtualServerRoutes implements VirtualServerRouteInterface +type FakeVirtualServerRoutes struct { + Fake *FakeK8sV1alpha1 + ns string +} + +var virtualserverroutesResource = schema.GroupVersionResource{Group: "k8s.nginx.org", Version: "v1alpha1", Resource: "virtualserverroutes"} + +var virtualserverroutesKind = schema.GroupVersionKind{Group: "k8s.nginx.org", Version: "v1alpha1", Kind: "VirtualServerRoute"} + +// Get takes name of the virtualServerRoute, and returns the corresponding virtualServerRoute object, and an error if there is any. +func (c *FakeVirtualServerRoutes) Get(name string, options v1.GetOptions) (result *v1alpha1.VirtualServerRoute, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(virtualserverroutesResource, c.ns, name), &v1alpha1.VirtualServerRoute{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.VirtualServerRoute), err +} + +// List takes label and field selectors, and returns the list of VirtualServerRoutes that match those selectors. +func (c *FakeVirtualServerRoutes) List(opts v1.ListOptions) (result *v1alpha1.VirtualServerRouteList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(virtualserverroutesResource, virtualserverroutesKind, c.ns, opts), &v1alpha1.VirtualServerRouteList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.VirtualServerRouteList{ListMeta: obj.(*v1alpha1.VirtualServerRouteList).ListMeta} + for _, item := range obj.(*v1alpha1.VirtualServerRouteList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested virtualServerRoutes. +func (c *FakeVirtualServerRoutes) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(virtualserverroutesResource, c.ns, opts)) + +} + +// Create takes the representation of a virtualServerRoute and creates it. Returns the server's representation of the virtualServerRoute, and an error, if there is any. +func (c *FakeVirtualServerRoutes) Create(virtualServerRoute *v1alpha1.VirtualServerRoute) (result *v1alpha1.VirtualServerRoute, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(virtualserverroutesResource, c.ns, virtualServerRoute), &v1alpha1.VirtualServerRoute{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.VirtualServerRoute), err +} + +// Update takes the representation of a virtualServerRoute and updates it. Returns the server's representation of the virtualServerRoute, and an error, if there is any. +func (c *FakeVirtualServerRoutes) Update(virtualServerRoute *v1alpha1.VirtualServerRoute) (result *v1alpha1.VirtualServerRoute, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(virtualserverroutesResource, c.ns, virtualServerRoute), &v1alpha1.VirtualServerRoute{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.VirtualServerRoute), err +} + +// Delete takes name of the virtualServerRoute and deletes it. Returns an error if one occurs. +func (c *FakeVirtualServerRoutes) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(virtualserverroutesResource, c.ns, name), &v1alpha1.VirtualServerRoute{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeVirtualServerRoutes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(virtualserverroutesResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.VirtualServerRouteList{}) + return err +} + +// Patch applies the patch and returns the patched virtualServerRoute. +func (c *FakeVirtualServerRoutes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.VirtualServerRoute, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(virtualserverroutesResource, c.ns, name, data, subresources...), &v1alpha1.VirtualServerRoute{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.VirtualServerRoute), err +} diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/generated_expansion.go new file mode 100644 index 0000000000..f1ce6f712a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/generated_expansion.go @@ -0,0 +1,7 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type VirtualServerExpansion interface{} + +type VirtualServerRouteExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/virtualserver.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/virtualserver.go new file mode 100644 index 0000000000..4ef0a5106b --- /dev/null +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/virtualserver.go @@ -0,0 +1,141 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + scheme "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// VirtualServersGetter has a method to return a VirtualServerInterface. +// A group's client should implement this interface. +type VirtualServersGetter interface { + VirtualServers(namespace string) VirtualServerInterface +} + +// VirtualServerInterface has methods to work with VirtualServer resources. +type VirtualServerInterface interface { + Create(*v1alpha1.VirtualServer) (*v1alpha1.VirtualServer, error) + Update(*v1alpha1.VirtualServer) (*v1alpha1.VirtualServer, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.VirtualServer, error) + List(opts v1.ListOptions) (*v1alpha1.VirtualServerList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.VirtualServer, err error) + VirtualServerExpansion +} + +// virtualServers implements VirtualServerInterface +type virtualServers struct { + client rest.Interface + ns string +} + +// newVirtualServers returns a VirtualServers +func newVirtualServers(c *K8sV1alpha1Client, namespace string) *virtualServers { + return &virtualServers{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the virtualServer, and returns the corresponding virtualServer object, and an error if there is any. +func (c *virtualServers) Get(name string, options v1.GetOptions) (result *v1alpha1.VirtualServer, err error) { + result = &v1alpha1.VirtualServer{} + err = c.client.Get(). + Namespace(c.ns). + Resource("virtualservers"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of VirtualServers that match those selectors. +func (c *virtualServers) List(opts v1.ListOptions) (result *v1alpha1.VirtualServerList, err error) { + result = &v1alpha1.VirtualServerList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("virtualservers"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested virtualServers. +func (c *virtualServers) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("virtualservers"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a virtualServer and creates it. Returns the server's representation of the virtualServer, and an error, if there is any. +func (c *virtualServers) Create(virtualServer *v1alpha1.VirtualServer) (result *v1alpha1.VirtualServer, err error) { + result = &v1alpha1.VirtualServer{} + err = c.client.Post(). + Namespace(c.ns). + Resource("virtualservers"). + Body(virtualServer). + Do(). + Into(result) + return +} + +// Update takes the representation of a virtualServer and updates it. Returns the server's representation of the virtualServer, and an error, if there is any. +func (c *virtualServers) Update(virtualServer *v1alpha1.VirtualServer) (result *v1alpha1.VirtualServer, err error) { + result = &v1alpha1.VirtualServer{} + err = c.client.Put(). + Namespace(c.ns). + Resource("virtualservers"). + Name(virtualServer.Name). + Body(virtualServer). + Do(). + Into(result) + return +} + +// Delete takes name of the virtualServer and deletes it. Returns an error if one occurs. +func (c *virtualServers) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("virtualservers"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *virtualServers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("virtualservers"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched virtualServer. +func (c *virtualServers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.VirtualServer, err error) { + result = &v1alpha1.VirtualServer{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("virtualservers"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/virtualserverroute.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/virtualserverroute.go new file mode 100644 index 0000000000..fe48853215 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/virtualserverroute.go @@ -0,0 +1,141 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + scheme "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// VirtualServerRoutesGetter has a method to return a VirtualServerRouteInterface. +// A group's client should implement this interface. +type VirtualServerRoutesGetter interface { + VirtualServerRoutes(namespace string) VirtualServerRouteInterface +} + +// VirtualServerRouteInterface has methods to work with VirtualServerRoute resources. +type VirtualServerRouteInterface interface { + Create(*v1alpha1.VirtualServerRoute) (*v1alpha1.VirtualServerRoute, error) + Update(*v1alpha1.VirtualServerRoute) (*v1alpha1.VirtualServerRoute, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.VirtualServerRoute, error) + List(opts v1.ListOptions) (*v1alpha1.VirtualServerRouteList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.VirtualServerRoute, err error) + VirtualServerRouteExpansion +} + +// virtualServerRoutes implements VirtualServerRouteInterface +type virtualServerRoutes struct { + client rest.Interface + ns string +} + +// newVirtualServerRoutes returns a VirtualServerRoutes +func newVirtualServerRoutes(c *K8sV1alpha1Client, namespace string) *virtualServerRoutes { + return &virtualServerRoutes{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the virtualServerRoute, and returns the corresponding virtualServerRoute object, and an error if there is any. +func (c *virtualServerRoutes) Get(name string, options v1.GetOptions) (result *v1alpha1.VirtualServerRoute, err error) { + result = &v1alpha1.VirtualServerRoute{} + err = c.client.Get(). + Namespace(c.ns). + Resource("virtualserverroutes"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of VirtualServerRoutes that match those selectors. +func (c *virtualServerRoutes) List(opts v1.ListOptions) (result *v1alpha1.VirtualServerRouteList, err error) { + result = &v1alpha1.VirtualServerRouteList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("virtualserverroutes"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested virtualServerRoutes. +func (c *virtualServerRoutes) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("virtualserverroutes"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a virtualServerRoute and creates it. Returns the server's representation of the virtualServerRoute, and an error, if there is any. +func (c *virtualServerRoutes) Create(virtualServerRoute *v1alpha1.VirtualServerRoute) (result *v1alpha1.VirtualServerRoute, err error) { + result = &v1alpha1.VirtualServerRoute{} + err = c.client.Post(). + Namespace(c.ns). + Resource("virtualserverroutes"). + Body(virtualServerRoute). + Do(). + Into(result) + return +} + +// Update takes the representation of a virtualServerRoute and updates it. Returns the server's representation of the virtualServerRoute, and an error, if there is any. +func (c *virtualServerRoutes) Update(virtualServerRoute *v1alpha1.VirtualServerRoute) (result *v1alpha1.VirtualServerRoute, err error) { + result = &v1alpha1.VirtualServerRoute{} + err = c.client.Put(). + Namespace(c.ns). + Resource("virtualserverroutes"). + Name(virtualServerRoute.Name). + Body(virtualServerRoute). + Do(). + Into(result) + return +} + +// Delete takes name of the virtualServerRoute and deletes it. Returns an error if one occurs. +func (c *virtualServerRoutes) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("virtualserverroutes"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *virtualServerRoutes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("virtualserverroutes"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched virtualServerRoute. +func (c *virtualServerRoutes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.VirtualServerRoute, err error) { + result = &v1alpha1.VirtualServerRoute{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("virtualserverroutes"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/configuration/interface.go b/pkg/client/informers/externalversions/configuration/interface.go new file mode 100644 index 0000000000..e5f688ca3f --- /dev/null +++ b/pkg/client/informers/externalversions/configuration/interface.go @@ -0,0 +1,30 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package k8s + +import ( + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/client/informers/externalversions/configuration/v1alpha1" + internalinterfaces "github.com/nginxinc/kubernetes-ingress/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/client/informers/externalversions/configuration/v1alpha1/interface.go b/pkg/client/informers/externalversions/configuration/v1alpha1/interface.go new file mode 100644 index 0000000000..7c46315298 --- /dev/null +++ b/pkg/client/informers/externalversions/configuration/v1alpha1/interface.go @@ -0,0 +1,36 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "github.com/nginxinc/kubernetes-ingress/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // VirtualServers returns a VirtualServerInformer. + VirtualServers() VirtualServerInformer + // VirtualServerRoutes returns a VirtualServerRouteInformer. + VirtualServerRoutes() VirtualServerRouteInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// VirtualServers returns a VirtualServerInformer. +func (v *version) VirtualServers() VirtualServerInformer { + return &virtualServerInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// VirtualServerRoutes returns a VirtualServerRouteInformer. +func (v *version) VirtualServerRoutes() VirtualServerRouteInformer { + return &virtualServerRouteInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/configuration/v1alpha1/virtualserver.go b/pkg/client/informers/externalversions/configuration/v1alpha1/virtualserver.go new file mode 100644 index 0000000000..ab5da54829 --- /dev/null +++ b/pkg/client/informers/externalversions/configuration/v1alpha1/virtualserver.go @@ -0,0 +1,73 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + configurationv1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + versioned "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned" + internalinterfaces "github.com/nginxinc/kubernetes-ingress/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/client/listers/configuration/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// VirtualServerInformer provides access to a shared informer and lister for +// VirtualServers. +type VirtualServerInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.VirtualServerLister +} + +type virtualServerInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewVirtualServerInformer constructs a new informer for VirtualServer type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewVirtualServerInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredVirtualServerInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredVirtualServerInformer constructs a new informer for VirtualServer type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredVirtualServerInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.K8sV1alpha1().VirtualServers(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.K8sV1alpha1().VirtualServers(namespace).Watch(options) + }, + }, + &configurationv1alpha1.VirtualServer{}, + resyncPeriod, + indexers, + ) +} + +func (f *virtualServerInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredVirtualServerInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *virtualServerInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&configurationv1alpha1.VirtualServer{}, f.defaultInformer) +} + +func (f *virtualServerInformer) Lister() v1alpha1.VirtualServerLister { + return v1alpha1.NewVirtualServerLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/configuration/v1alpha1/virtualserverroute.go b/pkg/client/informers/externalversions/configuration/v1alpha1/virtualserverroute.go new file mode 100644 index 0000000000..aba4b1a645 --- /dev/null +++ b/pkg/client/informers/externalversions/configuration/v1alpha1/virtualserverroute.go @@ -0,0 +1,73 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + configurationv1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + versioned "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned" + internalinterfaces "github.com/nginxinc/kubernetes-ingress/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/client/listers/configuration/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// VirtualServerRouteInformer provides access to a shared informer and lister for +// VirtualServerRoutes. +type VirtualServerRouteInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.VirtualServerRouteLister +} + +type virtualServerRouteInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewVirtualServerRouteInformer constructs a new informer for VirtualServerRoute type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewVirtualServerRouteInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredVirtualServerRouteInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredVirtualServerRouteInformer constructs a new informer for VirtualServerRoute type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredVirtualServerRouteInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.K8sV1alpha1().VirtualServerRoutes(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.K8sV1alpha1().VirtualServerRoutes(namespace).Watch(options) + }, + }, + &configurationv1alpha1.VirtualServerRoute{}, + resyncPeriod, + indexers, + ) +} + +func (f *virtualServerRouteInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredVirtualServerRouteInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *virtualServerRouteInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&configurationv1alpha1.VirtualServerRoute{}, f.defaultInformer) +} + +func (f *virtualServerRouteInformer) Lister() v1alpha1.VirtualServerRouteLister { + return v1alpha1.NewVirtualServerRouteLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go new file mode 100644 index 0000000000..88f33d6e47 --- /dev/null +++ b/pkg/client/informers/externalversions/factory.go @@ -0,0 +1,164 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + versioned "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned" + configuration "github.com/nginxinc/kubernetes-ingress/pkg/client/informers/externalversions/configuration" + internalinterfaces "github.com/nginxinc/kubernetes-ingress/pkg/client/informers/externalversions/internalinterfaces" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + K8s() configuration.Interface +} + +func (f *sharedInformerFactory) K8s() configuration.Interface { + return configuration.New(f, f.namespace, f.tweakListOptions) +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go new file mode 100644 index 0000000000..2f16b00560 --- /dev/null +++ b/pkg/client/informers/externalversions/generic.go @@ -0,0 +1,48 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=k8s.nginx.org, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("virtualservers"): + return &genericInformer{resource: resource.GroupResource(), informer: f.K8s().V1alpha1().VirtualServers().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("virtualserverroutes"): + return &genericInformer{resource: resource.GroupResource(), informer: f.K8s().V1alpha1().VirtualServerRoutes().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 0000000000..1e81499d8c --- /dev/null +++ b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,22 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + versioned "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" +) + +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/client/listers/configuration/v1alpha1/expansion_generated.go b/pkg/client/listers/configuration/v1alpha1/expansion_generated.go new file mode 100644 index 0000000000..9d9674eb4d --- /dev/null +++ b/pkg/client/listers/configuration/v1alpha1/expansion_generated.go @@ -0,0 +1,19 @@ +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +// VirtualServerListerExpansion allows custom methods to be added to +// VirtualServerLister. +type VirtualServerListerExpansion interface{} + +// VirtualServerNamespaceListerExpansion allows custom methods to be added to +// VirtualServerNamespaceLister. +type VirtualServerNamespaceListerExpansion interface{} + +// VirtualServerRouteListerExpansion allows custom methods to be added to +// VirtualServerRouteLister. +type VirtualServerRouteListerExpansion interface{} + +// VirtualServerRouteNamespaceListerExpansion allows custom methods to be added to +// VirtualServerRouteNamespaceLister. +type VirtualServerRouteNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/configuration/v1alpha1/virtualserver.go b/pkg/client/listers/configuration/v1alpha1/virtualserver.go new file mode 100644 index 0000000000..68b14180eb --- /dev/null +++ b/pkg/client/listers/configuration/v1alpha1/virtualserver.go @@ -0,0 +1,78 @@ +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// VirtualServerLister helps list VirtualServers. +type VirtualServerLister interface { + // List lists all VirtualServers in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.VirtualServer, err error) + // VirtualServers returns an object that can list and get VirtualServers. + VirtualServers(namespace string) VirtualServerNamespaceLister + VirtualServerListerExpansion +} + +// virtualServerLister implements the VirtualServerLister interface. +type virtualServerLister struct { + indexer cache.Indexer +} + +// NewVirtualServerLister returns a new VirtualServerLister. +func NewVirtualServerLister(indexer cache.Indexer) VirtualServerLister { + return &virtualServerLister{indexer: indexer} +} + +// List lists all VirtualServers in the indexer. +func (s *virtualServerLister) List(selector labels.Selector) (ret []*v1alpha1.VirtualServer, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.VirtualServer)) + }) + return ret, err +} + +// VirtualServers returns an object that can list and get VirtualServers. +func (s *virtualServerLister) VirtualServers(namespace string) VirtualServerNamespaceLister { + return virtualServerNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// VirtualServerNamespaceLister helps list and get VirtualServers. +type VirtualServerNamespaceLister interface { + // List lists all VirtualServers in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.VirtualServer, err error) + // Get retrieves the VirtualServer from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.VirtualServer, error) + VirtualServerNamespaceListerExpansion +} + +// virtualServerNamespaceLister implements the VirtualServerNamespaceLister +// interface. +type virtualServerNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all VirtualServers in the indexer for a given namespace. +func (s virtualServerNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.VirtualServer, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.VirtualServer)) + }) + return ret, err +} + +// Get retrieves the VirtualServer from the indexer for a given namespace and name. +func (s virtualServerNamespaceLister) Get(name string) (*v1alpha1.VirtualServer, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("virtualserver"), name) + } + return obj.(*v1alpha1.VirtualServer), nil +} diff --git a/pkg/client/listers/configuration/v1alpha1/virtualserverroute.go b/pkg/client/listers/configuration/v1alpha1/virtualserverroute.go new file mode 100644 index 0000000000..97f2ff28fa --- /dev/null +++ b/pkg/client/listers/configuration/v1alpha1/virtualserverroute.go @@ -0,0 +1,78 @@ +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// VirtualServerRouteLister helps list VirtualServerRoutes. +type VirtualServerRouteLister interface { + // List lists all VirtualServerRoutes in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.VirtualServerRoute, err error) + // VirtualServerRoutes returns an object that can list and get VirtualServerRoutes. + VirtualServerRoutes(namespace string) VirtualServerRouteNamespaceLister + VirtualServerRouteListerExpansion +} + +// virtualServerRouteLister implements the VirtualServerRouteLister interface. +type virtualServerRouteLister struct { + indexer cache.Indexer +} + +// NewVirtualServerRouteLister returns a new VirtualServerRouteLister. +func NewVirtualServerRouteLister(indexer cache.Indexer) VirtualServerRouteLister { + return &virtualServerRouteLister{indexer: indexer} +} + +// List lists all VirtualServerRoutes in the indexer. +func (s *virtualServerRouteLister) List(selector labels.Selector) (ret []*v1alpha1.VirtualServerRoute, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.VirtualServerRoute)) + }) + return ret, err +} + +// VirtualServerRoutes returns an object that can list and get VirtualServerRoutes. +func (s *virtualServerRouteLister) VirtualServerRoutes(namespace string) VirtualServerRouteNamespaceLister { + return virtualServerRouteNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// VirtualServerRouteNamespaceLister helps list and get VirtualServerRoutes. +type VirtualServerRouteNamespaceLister interface { + // List lists all VirtualServerRoutes in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.VirtualServerRoute, err error) + // Get retrieves the VirtualServerRoute from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.VirtualServerRoute, error) + VirtualServerRouteNamespaceListerExpansion +} + +// virtualServerRouteNamespaceLister implements the VirtualServerRouteNamespaceLister +// interface. +type virtualServerRouteNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all VirtualServerRoutes in the indexer for a given namespace. +func (s virtualServerRouteNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.VirtualServerRoute, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.VirtualServerRoute)) + }) + return ret, err +} + +// Get retrieves the VirtualServerRoute from the indexer for a given namespace and name. +func (s virtualServerRouteNamespaceLister) Get(name string) (*v1alpha1.VirtualServerRoute, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("virtualserverroute"), name) + } + return obj.(*v1alpha1.VirtualServerRoute), nil +} diff --git a/vendor/golang.org/x/tools/AUTHORS b/vendor/golang.org/x/tools/AUTHORS new file mode 100644 index 0000000000..15167cd746 --- /dev/null +++ b/vendor/golang.org/x/tools/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/tools/CONTRIBUTORS b/vendor/golang.org/x/tools/CONTRIBUTORS new file mode 100644 index 0000000000..1c4577e968 --- /dev/null +++ b/vendor/golang.org/x/tools/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/tools/LICENSE b/vendor/golang.org/x/tools/LICENSE new file mode 100644 index 0000000000..6a66aea5ea --- /dev/null +++ b/vendor/golang.org/x/tools/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/tools/PATENTS b/vendor/golang.org/x/tools/PATENTS new file mode 100644 index 0000000000..733099041f --- /dev/null +++ b/vendor/golang.org/x/tools/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/tools/cmd/getgo/LICENSE b/vendor/golang.org/x/tools/cmd/getgo/LICENSE new file mode 100644 index 0000000000..32017f8fa1 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/getgo/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2017 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go b/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go new file mode 100644 index 0000000000..6b7052b892 --- /dev/null +++ b/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go @@ -0,0 +1,627 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package astutil + +// This file defines utilities for working with source positions. + +import ( + "fmt" + "go/ast" + "go/token" + "sort" +) + +// PathEnclosingInterval returns the node that encloses the source +// interval [start, end), and all its ancestors up to the AST root. +// +// The definition of "enclosing" used by this function considers +// additional whitespace abutting a node to be enclosed by it. +// In this example: +// +// z := x + y // add them +// <-A-> +// <----B-----> +// +// the ast.BinaryExpr(+) node is considered to enclose interval B +// even though its [Pos()..End()) is actually only interval A. +// This behaviour makes user interfaces more tolerant of imperfect +// input. +// +// This function treats tokens as nodes, though they are not included +// in the result. e.g. PathEnclosingInterval("+") returns the +// enclosing ast.BinaryExpr("x + y"). +// +// If start==end, the 1-char interval following start is used instead. +// +// The 'exact' result is true if the interval contains only path[0] +// and perhaps some adjacent whitespace. It is false if the interval +// overlaps multiple children of path[0], or if it contains only +// interior whitespace of path[0]. +// In this example: +// +// z := x + y // add them +// <--C--> <---E--> +// ^ +// D +// +// intervals C, D and E are inexact. C is contained by the +// z-assignment statement, because it spans three of its children (:=, +// x, +). So too is the 1-char interval D, because it contains only +// interior whitespace of the assignment. E is considered interior +// whitespace of the BlockStmt containing the assignment. +// +// Precondition: [start, end) both lie within the same file as root. +// TODO(adonovan): return (nil, false) in this case and remove precond. +// Requires FileSet; see loader.tokenFileContainsPos. +// +// Postcondition: path is never nil; it always contains at least 'root'. +// +func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) { + // fmt.Printf("EnclosingInterval %d %d\n", start, end) // debugging + + // Precondition: node.[Pos..End) and adjoining whitespace contain [start, end). + var visit func(node ast.Node) bool + visit = func(node ast.Node) bool { + path = append(path, node) + + nodePos := node.Pos() + nodeEnd := node.End() + + // fmt.Printf("visit(%T, %d, %d)\n", node, nodePos, nodeEnd) // debugging + + // Intersect [start, end) with interval of node. + if start < nodePos { + start = nodePos + } + if end > nodeEnd { + end = nodeEnd + } + + // Find sole child that contains [start, end). + children := childrenOf(node) + l := len(children) + for i, child := range children { + // [childPos, childEnd) is unaugmented interval of child. + childPos := child.Pos() + childEnd := child.End() + + // [augPos, augEnd) is whitespace-augmented interval of child. + augPos := childPos + augEnd := childEnd + if i > 0 { + augPos = children[i-1].End() // start of preceding whitespace + } + if i < l-1 { + nextChildPos := children[i+1].Pos() + // Does [start, end) lie between child and next child? + if start >= augEnd && end <= nextChildPos { + return false // inexact match + } + augEnd = nextChildPos // end of following whitespace + } + + // fmt.Printf("\tchild %d: [%d..%d)\tcontains interval [%d..%d)?\n", + // i, augPos, augEnd, start, end) // debugging + + // Does augmented child strictly contain [start, end)? + if augPos <= start && end <= augEnd { + _, isToken := child.(tokenNode) + return isToken || visit(child) + } + + // Does [start, end) overlap multiple children? + // i.e. left-augmented child contains start + // but LR-augmented child does not contain end. + if start < childEnd && end > augEnd { + break + } + } + + // No single child contained [start, end), + // so node is the result. Is it exact? + + // (It's tempting to put this condition before the + // child loop, but it gives the wrong result in the + // case where a node (e.g. ExprStmt) and its sole + // child have equal intervals.) + if start == nodePos && end == nodeEnd { + return true // exact match + } + + return false // inexact: overlaps multiple children + } + + if start > end { + start, end = end, start + } + + if start < root.End() && end > root.Pos() { + if start == end { + end = start + 1 // empty interval => interval of size 1 + } + exact = visit(root) + + // Reverse the path: + for i, l := 0, len(path); i < l/2; i++ { + path[i], path[l-1-i] = path[l-1-i], path[i] + } + } else { + // Selection lies within whitespace preceding the + // first (or following the last) declaration in the file. + // The result nonetheless always includes the ast.File. + path = append(path, root) + } + + return +} + +// tokenNode is a dummy implementation of ast.Node for a single token. +// They are used transiently by PathEnclosingInterval but never escape +// this package. +// +type tokenNode struct { + pos token.Pos + end token.Pos +} + +func (n tokenNode) Pos() token.Pos { + return n.pos +} + +func (n tokenNode) End() token.Pos { + return n.end +} + +func tok(pos token.Pos, len int) ast.Node { + return tokenNode{pos, pos + token.Pos(len)} +} + +// childrenOf returns the direct non-nil children of ast.Node n. +// It may include fake ast.Node implementations for bare tokens. +// it is not safe to call (e.g.) ast.Walk on such nodes. +// +func childrenOf(n ast.Node) []ast.Node { + var children []ast.Node + + // First add nodes for all true subtrees. + ast.Inspect(n, func(node ast.Node) bool { + if node == n { // push n + return true // recur + } + if node != nil { // push child + children = append(children, node) + } + return false // no recursion + }) + + // Then add fake Nodes for bare tokens. + switch n := n.(type) { + case *ast.ArrayType: + children = append(children, + tok(n.Lbrack, len("[")), + tok(n.Elt.End(), len("]"))) + + case *ast.AssignStmt: + children = append(children, + tok(n.TokPos, len(n.Tok.String()))) + + case *ast.BasicLit: + children = append(children, + tok(n.ValuePos, len(n.Value))) + + case *ast.BinaryExpr: + children = append(children, tok(n.OpPos, len(n.Op.String()))) + + case *ast.BlockStmt: + children = append(children, + tok(n.Lbrace, len("{")), + tok(n.Rbrace, len("}"))) + + case *ast.BranchStmt: + children = append(children, + tok(n.TokPos, len(n.Tok.String()))) + + case *ast.CallExpr: + children = append(children, + tok(n.Lparen, len("(")), + tok(n.Rparen, len(")"))) + if n.Ellipsis != 0 { + children = append(children, tok(n.Ellipsis, len("..."))) + } + + case *ast.CaseClause: + if n.List == nil { + children = append(children, + tok(n.Case, len("default"))) + } else { + children = append(children, + tok(n.Case, len("case"))) + } + children = append(children, tok(n.Colon, len(":"))) + + case *ast.ChanType: + switch n.Dir { + case ast.RECV: + children = append(children, tok(n.Begin, len("<-chan"))) + case ast.SEND: + children = append(children, tok(n.Begin, len("chan<-"))) + case ast.RECV | ast.SEND: + children = append(children, tok(n.Begin, len("chan"))) + } + + case *ast.CommClause: + if n.Comm == nil { + children = append(children, + tok(n.Case, len("default"))) + } else { + children = append(children, + tok(n.Case, len("case"))) + } + children = append(children, tok(n.Colon, len(":"))) + + case *ast.Comment: + // nop + + case *ast.CommentGroup: + // nop + + case *ast.CompositeLit: + children = append(children, + tok(n.Lbrace, len("{")), + tok(n.Rbrace, len("{"))) + + case *ast.DeclStmt: + // nop + + case *ast.DeferStmt: + children = append(children, + tok(n.Defer, len("defer"))) + + case *ast.Ellipsis: + children = append(children, + tok(n.Ellipsis, len("..."))) + + case *ast.EmptyStmt: + // nop + + case *ast.ExprStmt: + // nop + + case *ast.Field: + // TODO(adonovan): Field.{Doc,Comment,Tag}? + + case *ast.FieldList: + children = append(children, + tok(n.Opening, len("(")), + tok(n.Closing, len(")"))) + + case *ast.File: + // TODO test: Doc + children = append(children, + tok(n.Package, len("package"))) + + case *ast.ForStmt: + children = append(children, + tok(n.For, len("for"))) + + case *ast.FuncDecl: + // TODO(adonovan): FuncDecl.Comment? + + // Uniquely, FuncDecl breaks the invariant that + // preorder traversal yields tokens in lexical order: + // in fact, FuncDecl.Recv precedes FuncDecl.Type.Func. + // + // As a workaround, we inline the case for FuncType + // here and order things correctly. + // + children = nil // discard ast.Walk(FuncDecl) info subtrees + children = append(children, tok(n.Type.Func, len("func"))) + if n.Recv != nil { + children = append(children, n.Recv) + } + children = append(children, n.Name) + if n.Type.Params != nil { + children = append(children, n.Type.Params) + } + if n.Type.Results != nil { + children = append(children, n.Type.Results) + } + if n.Body != nil { + children = append(children, n.Body) + } + + case *ast.FuncLit: + // nop + + case *ast.FuncType: + if n.Func != 0 { + children = append(children, + tok(n.Func, len("func"))) + } + + case *ast.GenDecl: + children = append(children, + tok(n.TokPos, len(n.Tok.String()))) + if n.Lparen != 0 { + children = append(children, + tok(n.Lparen, len("(")), + tok(n.Rparen, len(")"))) + } + + case *ast.GoStmt: + children = append(children, + tok(n.Go, len("go"))) + + case *ast.Ident: + children = append(children, + tok(n.NamePos, len(n.Name))) + + case *ast.IfStmt: + children = append(children, + tok(n.If, len("if"))) + + case *ast.ImportSpec: + // TODO(adonovan): ImportSpec.{Doc,EndPos}? + + case *ast.IncDecStmt: + children = append(children, + tok(n.TokPos, len(n.Tok.String()))) + + case *ast.IndexExpr: + children = append(children, + tok(n.Lbrack, len("{")), + tok(n.Rbrack, len("}"))) + + case *ast.InterfaceType: + children = append(children, + tok(n.Interface, len("interface"))) + + case *ast.KeyValueExpr: + children = append(children, + tok(n.Colon, len(":"))) + + case *ast.LabeledStmt: + children = append(children, + tok(n.Colon, len(":"))) + + case *ast.MapType: + children = append(children, + tok(n.Map, len("map"))) + + case *ast.ParenExpr: + children = append(children, + tok(n.Lparen, len("(")), + tok(n.Rparen, len(")"))) + + case *ast.RangeStmt: + children = append(children, + tok(n.For, len("for")), + tok(n.TokPos, len(n.Tok.String()))) + + case *ast.ReturnStmt: + children = append(children, + tok(n.Return, len("return"))) + + case *ast.SelectStmt: + children = append(children, + tok(n.Select, len("select"))) + + case *ast.SelectorExpr: + // nop + + case *ast.SendStmt: + children = append(children, + tok(n.Arrow, len("<-"))) + + case *ast.SliceExpr: + children = append(children, + tok(n.Lbrack, len("[")), + tok(n.Rbrack, len("]"))) + + case *ast.StarExpr: + children = append(children, tok(n.Star, len("*"))) + + case *ast.StructType: + children = append(children, tok(n.Struct, len("struct"))) + + case *ast.SwitchStmt: + children = append(children, tok(n.Switch, len("switch"))) + + case *ast.TypeAssertExpr: + children = append(children, + tok(n.Lparen-1, len(".")), + tok(n.Lparen, len("(")), + tok(n.Rparen, len(")"))) + + case *ast.TypeSpec: + // TODO(adonovan): TypeSpec.{Doc,Comment}? + + case *ast.TypeSwitchStmt: + children = append(children, tok(n.Switch, len("switch"))) + + case *ast.UnaryExpr: + children = append(children, tok(n.OpPos, len(n.Op.String()))) + + case *ast.ValueSpec: + // TODO(adonovan): ValueSpec.{Doc,Comment}? + + case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt: + // nop + } + + // TODO(adonovan): opt: merge the logic of ast.Inspect() into + // the switch above so we can make interleaved callbacks for + // both Nodes and Tokens in the right order and avoid the need + // to sort. + sort.Sort(byPos(children)) + + return children +} + +type byPos []ast.Node + +func (sl byPos) Len() int { + return len(sl) +} +func (sl byPos) Less(i, j int) bool { + return sl[i].Pos() < sl[j].Pos() +} +func (sl byPos) Swap(i, j int) { + sl[i], sl[j] = sl[j], sl[i] +} + +// NodeDescription returns a description of the concrete type of n suitable +// for a user interface. +// +// TODO(adonovan): in some cases (e.g. Field, FieldList, Ident, +// StarExpr) we could be much more specific given the path to the AST +// root. Perhaps we should do that. +// +func NodeDescription(n ast.Node) string { + switch n := n.(type) { + case *ast.ArrayType: + return "array type" + case *ast.AssignStmt: + return "assignment" + case *ast.BadDecl: + return "bad declaration" + case *ast.BadExpr: + return "bad expression" + case *ast.BadStmt: + return "bad statement" + case *ast.BasicLit: + return "basic literal" + case *ast.BinaryExpr: + return fmt.Sprintf("binary %s operation", n.Op) + case *ast.BlockStmt: + return "block" + case *ast.BranchStmt: + switch n.Tok { + case token.BREAK: + return "break statement" + case token.CONTINUE: + return "continue statement" + case token.GOTO: + return "goto statement" + case token.FALLTHROUGH: + return "fall-through statement" + } + case *ast.CallExpr: + if len(n.Args) == 1 && !n.Ellipsis.IsValid() { + return "function call (or conversion)" + } + return "function call" + case *ast.CaseClause: + return "case clause" + case *ast.ChanType: + return "channel type" + case *ast.CommClause: + return "communication clause" + case *ast.Comment: + return "comment" + case *ast.CommentGroup: + return "comment group" + case *ast.CompositeLit: + return "composite literal" + case *ast.DeclStmt: + return NodeDescription(n.Decl) + " statement" + case *ast.DeferStmt: + return "defer statement" + case *ast.Ellipsis: + return "ellipsis" + case *ast.EmptyStmt: + return "empty statement" + case *ast.ExprStmt: + return "expression statement" + case *ast.Field: + // Can be any of these: + // struct {x, y int} -- struct field(s) + // struct {T} -- anon struct field + // interface {I} -- interface embedding + // interface {f()} -- interface method + // func (A) func(B) C -- receiver, param(s), result(s) + return "field/method/parameter" + case *ast.FieldList: + return "field/method/parameter list" + case *ast.File: + return "source file" + case *ast.ForStmt: + return "for loop" + case *ast.FuncDecl: + return "function declaration" + case *ast.FuncLit: + return "function literal" + case *ast.FuncType: + return "function type" + case *ast.GenDecl: + switch n.Tok { + case token.IMPORT: + return "import declaration" + case token.CONST: + return "constant declaration" + case token.TYPE: + return "type declaration" + case token.VAR: + return "variable declaration" + } + case *ast.GoStmt: + return "go statement" + case *ast.Ident: + return "identifier" + case *ast.IfStmt: + return "if statement" + case *ast.ImportSpec: + return "import specification" + case *ast.IncDecStmt: + if n.Tok == token.INC { + return "increment statement" + } + return "decrement statement" + case *ast.IndexExpr: + return "index expression" + case *ast.InterfaceType: + return "interface type" + case *ast.KeyValueExpr: + return "key/value association" + case *ast.LabeledStmt: + return "statement label" + case *ast.MapType: + return "map type" + case *ast.Package: + return "package" + case *ast.ParenExpr: + return "parenthesized " + NodeDescription(n.X) + case *ast.RangeStmt: + return "range loop" + case *ast.ReturnStmt: + return "return statement" + case *ast.SelectStmt: + return "select statement" + case *ast.SelectorExpr: + return "selector" + case *ast.SendStmt: + return "channel send" + case *ast.SliceExpr: + return "slice expression" + case *ast.StarExpr: + return "*-operation" // load/store expr or pointer type + case *ast.StructType: + return "struct type" + case *ast.SwitchStmt: + return "switch statement" + case *ast.TypeAssertExpr: + return "type assertion" + case *ast.TypeSpec: + return "type specification" + case *ast.TypeSwitchStmt: + return "type switch" + case *ast.UnaryExpr: + return fmt.Sprintf("unary %s operation", n.Op) + case *ast.ValueSpec: + return "value specification" + + } + panic(fmt.Sprintf("unexpected node type: %T", n)) +} diff --git a/vendor/golang.org/x/tools/go/ast/astutil/imports.go b/vendor/golang.org/x/tools/go/ast/astutil/imports.go new file mode 100644 index 0000000000..3e4b195368 --- /dev/null +++ b/vendor/golang.org/x/tools/go/ast/astutil/imports.go @@ -0,0 +1,481 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package astutil contains common utilities for working with the Go AST. +package astutil // import "golang.org/x/tools/go/ast/astutil" + +import ( + "fmt" + "go/ast" + "go/token" + "strconv" + "strings" +) + +// AddImport adds the import path to the file f, if absent. +func AddImport(fset *token.FileSet, f *ast.File, path string) (added bool) { + return AddNamedImport(fset, f, "", path) +} + +// AddNamedImport adds the import with the given name and path to the file f, if absent. +// If name is not empty, it is used to rename the import. +// +// For example, calling +// AddNamedImport(fset, f, "pathpkg", "path") +// adds +// import pathpkg "path" +func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (added bool) { + if imports(f, name, path) { + return false + } + + newImport := &ast.ImportSpec{ + Path: &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote(path), + }, + } + if name != "" { + newImport.Name = &ast.Ident{Name: name} + } + + // Find an import decl to add to. + // The goal is to find an existing import + // whose import path has the longest shared + // prefix with path. + var ( + bestMatch = -1 // length of longest shared prefix + lastImport = -1 // index in f.Decls of the file's final import decl + impDecl *ast.GenDecl // import decl containing the best match + impIndex = -1 // spec index in impDecl containing the best match + + isThirdPartyPath = isThirdParty(path) + ) + for i, decl := range f.Decls { + gen, ok := decl.(*ast.GenDecl) + if ok && gen.Tok == token.IMPORT { + lastImport = i + // Do not add to import "C", to avoid disrupting the + // association with its doc comment, breaking cgo. + if declImports(gen, "C") { + continue + } + + // Match an empty import decl if that's all that is available. + if len(gen.Specs) == 0 && bestMatch == -1 { + impDecl = gen + } + + // Compute longest shared prefix with imports in this group and find best + // matched import spec. + // 1. Always prefer import spec with longest shared prefix. + // 2. While match length is 0, + // - for stdlib package: prefer first import spec. + // - for third party package: prefer first third party import spec. + // We cannot use last import spec as best match for third party package + // because grouped imports are usually placed last by goimports -local + // flag. + // See issue #19190. + seenAnyThirdParty := false + for j, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + p := importPath(impspec) + n := matchLen(p, path) + if n > bestMatch || (bestMatch == 0 && !seenAnyThirdParty && isThirdPartyPath) { + bestMatch = n + impDecl = gen + impIndex = j + } + seenAnyThirdParty = seenAnyThirdParty || isThirdParty(p) + } + } + } + + // If no import decl found, add one after the last import. + if impDecl == nil { + impDecl = &ast.GenDecl{ + Tok: token.IMPORT, + } + if lastImport >= 0 { + impDecl.TokPos = f.Decls[lastImport].End() + } else { + // There are no existing imports. + // Our new import, preceded by a blank line, goes after the package declaration + // and after the comment, if any, that starts on the same line as the + // package declaration. + impDecl.TokPos = f.Package + + file := fset.File(f.Package) + pkgLine := file.Line(f.Package) + for _, c := range f.Comments { + if file.Line(c.Pos()) > pkgLine { + break + } + // +2 for a blank line + impDecl.TokPos = c.End() + 2 + } + } + f.Decls = append(f.Decls, nil) + copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:]) + f.Decls[lastImport+1] = impDecl + } + + // Insert new import at insertAt. + insertAt := 0 + if impIndex >= 0 { + // insert after the found import + insertAt = impIndex + 1 + } + impDecl.Specs = append(impDecl.Specs, nil) + copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:]) + impDecl.Specs[insertAt] = newImport + pos := impDecl.Pos() + if insertAt > 0 { + // If there is a comment after an existing import, preserve the comment + // position by adding the new import after the comment. + if spec, ok := impDecl.Specs[insertAt-1].(*ast.ImportSpec); ok && spec.Comment != nil { + pos = spec.Comment.End() + } else { + // Assign same position as the previous import, + // so that the sorter sees it as being in the same block. + pos = impDecl.Specs[insertAt-1].Pos() + } + } + if newImport.Name != nil { + newImport.Name.NamePos = pos + } + newImport.Path.ValuePos = pos + newImport.EndPos = pos + + // Clean up parens. impDecl contains at least one spec. + if len(impDecl.Specs) == 1 { + // Remove unneeded parens. + impDecl.Lparen = token.NoPos + } else if !impDecl.Lparen.IsValid() { + // impDecl needs parens added. + impDecl.Lparen = impDecl.Specs[0].Pos() + } + + f.Imports = append(f.Imports, newImport) + + if len(f.Decls) <= 1 { + return true + } + + // Merge all the import declarations into the first one. + var first *ast.GenDecl + for i := 0; i < len(f.Decls); i++ { + decl := f.Decls[i] + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") { + continue + } + if first == nil { + first = gen + continue // Don't touch the first one. + } + // We now know there is more than one package in this import + // declaration. Ensure that it ends up parenthesized. + first.Lparen = first.Pos() + // Move the imports of the other import declaration to the first one. + for _, spec := range gen.Specs { + spec.(*ast.ImportSpec).Path.ValuePos = first.Pos() + first.Specs = append(first.Specs, spec) + } + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + i-- + } + + return true +} + +func isThirdParty(importPath string) bool { + // Third party package import path usually contains "." (".com", ".org", ...) + // This logic is taken from golang.org/x/tools/imports package. + return strings.Contains(importPath, ".") +} + +// DeleteImport deletes the import path from the file f, if present. +// If there are duplicate import declarations, all matching ones are deleted. +func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) { + return DeleteNamedImport(fset, f, "", path) +} + +// DeleteNamedImport deletes the import with the given name and path from the file f, if present. +// If there are duplicate import declarations, all matching ones are deleted. +func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) { + var delspecs []*ast.ImportSpec + var delcomments []*ast.CommentGroup + + // Find the import nodes that import path, if any. + for i := 0; i < len(f.Decls); i++ { + decl := f.Decls[i] + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.IMPORT { + continue + } + for j := 0; j < len(gen.Specs); j++ { + spec := gen.Specs[j] + impspec := spec.(*ast.ImportSpec) + if importName(impspec) != name || importPath(impspec) != path { + continue + } + + // We found an import spec that imports path. + // Delete it. + delspecs = append(delspecs, impspec) + deleted = true + copy(gen.Specs[j:], gen.Specs[j+1:]) + gen.Specs = gen.Specs[:len(gen.Specs)-1] + + // If this was the last import spec in this decl, + // delete the decl, too. + if len(gen.Specs) == 0 { + copy(f.Decls[i:], f.Decls[i+1:]) + f.Decls = f.Decls[:len(f.Decls)-1] + i-- + break + } else if len(gen.Specs) == 1 { + if impspec.Doc != nil { + delcomments = append(delcomments, impspec.Doc) + } + if impspec.Comment != nil { + delcomments = append(delcomments, impspec.Comment) + } + for _, cg := range f.Comments { + // Found comment on the same line as the import spec. + if cg.End() < impspec.Pos() && fset.Position(cg.End()).Line == fset.Position(impspec.Pos()).Line { + delcomments = append(delcomments, cg) + break + } + } + + spec := gen.Specs[0].(*ast.ImportSpec) + + // Move the documentation right after the import decl. + if spec.Doc != nil { + for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Doc.Pos()).Line { + fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line) + } + } + for _, cg := range f.Comments { + if cg.End() < spec.Pos() && fset.Position(cg.End()).Line == fset.Position(spec.Pos()).Line { + for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Pos()).Line { + fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line) + } + break + } + } + } + if j > 0 { + lastImpspec := gen.Specs[j-1].(*ast.ImportSpec) + lastLine := fset.Position(lastImpspec.Path.ValuePos).Line + line := fset.Position(impspec.Path.ValuePos).Line + + // We deleted an entry but now there may be + // a blank line-sized hole where the import was. + if line-lastLine > 1 { + // There was a blank line immediately preceding the deleted import, + // so there's no need to close the hole. + // Do nothing. + } else if line != fset.File(gen.Rparen).LineCount() { + // There was no blank line. Close the hole. + fset.File(gen.Rparen).MergeLine(line) + } + } + j-- + } + } + + // Delete imports from f.Imports. + for i := 0; i < len(f.Imports); i++ { + imp := f.Imports[i] + for j, del := range delspecs { + if imp == del { + copy(f.Imports[i:], f.Imports[i+1:]) + f.Imports = f.Imports[:len(f.Imports)-1] + copy(delspecs[j:], delspecs[j+1:]) + delspecs = delspecs[:len(delspecs)-1] + i-- + break + } + } + } + + // Delete comments from f.Comments. + for i := 0; i < len(f.Comments); i++ { + cg := f.Comments[i] + for j, del := range delcomments { + if cg == del { + copy(f.Comments[i:], f.Comments[i+1:]) + f.Comments = f.Comments[:len(f.Comments)-1] + copy(delcomments[j:], delcomments[j+1:]) + delcomments = delcomments[:len(delcomments)-1] + i-- + break + } + } + } + + if len(delspecs) > 0 { + panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs)) + } + + return +} + +// RewriteImport rewrites any import of path oldPath to path newPath. +func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) { + for _, imp := range f.Imports { + if importPath(imp) == oldPath { + rewrote = true + // record old End, because the default is to compute + // it using the length of imp.Path.Value. + imp.EndPos = imp.End() + imp.Path.Value = strconv.Quote(newPath) + } + } + return +} + +// UsesImport reports whether a given import is used. +func UsesImport(f *ast.File, path string) (used bool) { + spec := importSpec(f, path) + if spec == nil { + return + } + + name := spec.Name.String() + switch name { + case "": + // If the package name is not explicitly specified, + // make an educated guess. This is not guaranteed to be correct. + lastSlash := strings.LastIndex(path, "/") + if lastSlash == -1 { + name = path + } else { + name = path[lastSlash+1:] + } + case "_", ".": + // Not sure if this import is used - err on the side of caution. + return true + } + + ast.Walk(visitFn(func(n ast.Node) { + sel, ok := n.(*ast.SelectorExpr) + if ok && isTopName(sel.X, name) { + used = true + } + }), f) + + return +} + +type visitFn func(node ast.Node) + +func (fn visitFn) Visit(node ast.Node) ast.Visitor { + fn(node) + return fn +} + +// imports reports whether f has an import with the specified name and path. +func imports(f *ast.File, name, path string) bool { + for _, s := range f.Imports { + if importName(s) == name && importPath(s) == path { + return true + } + } + return false +} + +// importSpec returns the import spec if f imports path, +// or nil otherwise. +func importSpec(f *ast.File, path string) *ast.ImportSpec { + for _, s := range f.Imports { + if importPath(s) == path { + return s + } + } + return nil +} + +// importName returns the name of s, +// or "" if the import is not named. +func importName(s *ast.ImportSpec) string { + if s.Name == nil { + return "" + } + return s.Name.Name +} + +// importPath returns the unquoted import path of s, +// or "" if the path is not properly quoted. +func importPath(s *ast.ImportSpec) string { + t, err := strconv.Unquote(s.Path.Value) + if err != nil { + return "" + } + return t +} + +// declImports reports whether gen contains an import of path. +func declImports(gen *ast.GenDecl, path string) bool { + if gen.Tok != token.IMPORT { + return false + } + for _, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + if importPath(impspec) == path { + return true + } + } + return false +} + +// matchLen returns the length of the longest path segment prefix shared by x and y. +func matchLen(x, y string) int { + n := 0 + for i := 0; i < len(x) && i < len(y) && x[i] == y[i]; i++ { + if x[i] == '/' { + n++ + } + } + return n +} + +// isTopName returns true if n is a top-level unresolved identifier with the given name. +func isTopName(n ast.Expr, name string) bool { + id, ok := n.(*ast.Ident) + return ok && id.Name == name && id.Obj == nil +} + +// Imports returns the file imports grouped by paragraph. +func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec { + var groups [][]*ast.ImportSpec + + for _, decl := range f.Decls { + genDecl, ok := decl.(*ast.GenDecl) + if !ok || genDecl.Tok != token.IMPORT { + break + } + + group := []*ast.ImportSpec{} + + var lastLine int + for _, spec := range genDecl.Specs { + importSpec := spec.(*ast.ImportSpec) + pos := importSpec.Path.ValuePos + line := fset.Position(pos).Line + if lastLine > 0 && pos > 0 && line-lastLine > 1 { + groups = append(groups, group) + group = []*ast.ImportSpec{} + } + group = append(group, importSpec) + lastLine = line + } + groups = append(groups, group) + } + + return groups +} diff --git a/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go b/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go new file mode 100644 index 0000000000..cf72ea990b --- /dev/null +++ b/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go @@ -0,0 +1,477 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package astutil + +import ( + "fmt" + "go/ast" + "reflect" + "sort" +) + +// An ApplyFunc is invoked by Apply for each node n, even if n is nil, +// before and/or after the node's children, using a Cursor describing +// the current node and providing operations on it. +// +// The return value of ApplyFunc controls the syntax tree traversal. +// See Apply for details. +type ApplyFunc func(*Cursor) bool + +// Apply traverses a syntax tree recursively, starting with root, +// and calling pre and post for each node as described below. +// Apply returns the syntax tree, possibly modified. +// +// If pre is not nil, it is called for each node before the node's +// children are traversed (pre-order). If pre returns false, no +// children are traversed, and post is not called for that node. +// +// If post is not nil, and a prior call of pre didn't return false, +// post is called for each node after its children are traversed +// (post-order). If post returns false, traversal is terminated and +// Apply returns immediately. +// +// Only fields that refer to AST nodes are considered children; +// i.e., token.Pos, Scopes, Objects, and fields of basic types +// (strings, etc.) are ignored. +// +// Children are traversed in the order in which they appear in the +// respective node's struct definition. A package's files are +// traversed in the filenames' alphabetical order. +// +func Apply(root ast.Node, pre, post ApplyFunc) (result ast.Node) { + parent := &struct{ ast.Node }{root} + defer func() { + if r := recover(); r != nil && r != abort { + panic(r) + } + result = parent.Node + }() + a := &application{pre: pre, post: post} + a.apply(parent, "Node", nil, root) + return +} + +var abort = new(int) // singleton, to signal termination of Apply + +// A Cursor describes a node encountered during Apply. +// Information about the node and its parent is available +// from the Node, Parent, Name, and Index methods. +// +// If p is a variable of type and value of the current parent node +// c.Parent(), and f is the field identifier with name c.Name(), +// the following invariants hold: +// +// p.f == c.Node() if c.Index() < 0 +// p.f[c.Index()] == c.Node() if c.Index() >= 0 +// +// The methods Replace, Delete, InsertBefore, and InsertAfter +// can be used to change the AST without disrupting Apply. +type Cursor struct { + parent ast.Node + name string + iter *iterator // valid if non-nil + node ast.Node +} + +// Node returns the current Node. +func (c *Cursor) Node() ast.Node { return c.node } + +// Parent returns the parent of the current Node. +func (c *Cursor) Parent() ast.Node { return c.parent } + +// Name returns the name of the parent Node field that contains the current Node. +// If the parent is a *ast.Package and the current Node is a *ast.File, Name returns +// the filename for the current Node. +func (c *Cursor) Name() string { return c.name } + +// Index reports the index >= 0 of the current Node in the slice of Nodes that +// contains it, or a value < 0 if the current Node is not part of a slice. +// The index of the current node changes if InsertBefore is called while +// processing the current node. +func (c *Cursor) Index() int { + if c.iter != nil { + return c.iter.index + } + return -1 +} + +// field returns the current node's parent field value. +func (c *Cursor) field() reflect.Value { + return reflect.Indirect(reflect.ValueOf(c.parent)).FieldByName(c.name) +} + +// Replace replaces the current Node with n. +// The replacement node is not walked by Apply. +func (c *Cursor) Replace(n ast.Node) { + if _, ok := c.node.(*ast.File); ok { + file, ok := n.(*ast.File) + if !ok { + panic("attempt to replace *ast.File with non-*ast.File") + } + c.parent.(*ast.Package).Files[c.name] = file + return + } + + v := c.field() + if i := c.Index(); i >= 0 { + v = v.Index(i) + } + v.Set(reflect.ValueOf(n)) +} + +// Delete deletes the current Node from its containing slice. +// If the current Node is not part of a slice, Delete panics. +// As a special case, if the current node is a package file, +// Delete removes it from the package's Files map. +func (c *Cursor) Delete() { + if _, ok := c.node.(*ast.File); ok { + delete(c.parent.(*ast.Package).Files, c.name) + return + } + + i := c.Index() + if i < 0 { + panic("Delete node not contained in slice") + } + v := c.field() + l := v.Len() + reflect.Copy(v.Slice(i, l), v.Slice(i+1, l)) + v.Index(l - 1).Set(reflect.Zero(v.Type().Elem())) + v.SetLen(l - 1) + c.iter.step-- +} + +// InsertAfter inserts n after the current Node in its containing slice. +// If the current Node is not part of a slice, InsertAfter panics. +// Apply does not walk n. +func (c *Cursor) InsertAfter(n ast.Node) { + i := c.Index() + if i < 0 { + panic("InsertAfter node not contained in slice") + } + v := c.field() + v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem()))) + l := v.Len() + reflect.Copy(v.Slice(i+2, l), v.Slice(i+1, l)) + v.Index(i + 1).Set(reflect.ValueOf(n)) + c.iter.step++ +} + +// InsertBefore inserts n before the current Node in its containing slice. +// If the current Node is not part of a slice, InsertBefore panics. +// Apply will not walk n. +func (c *Cursor) InsertBefore(n ast.Node) { + i := c.Index() + if i < 0 { + panic("InsertBefore node not contained in slice") + } + v := c.field() + v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem()))) + l := v.Len() + reflect.Copy(v.Slice(i+1, l), v.Slice(i, l)) + v.Index(i).Set(reflect.ValueOf(n)) + c.iter.index++ +} + +// application carries all the shared data so we can pass it around cheaply. +type application struct { + pre, post ApplyFunc + cursor Cursor + iter iterator +} + +func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast.Node) { + // convert typed nil into untyped nil + if v := reflect.ValueOf(n); v.Kind() == reflect.Ptr && v.IsNil() { + n = nil + } + + // avoid heap-allocating a new cursor for each apply call; reuse a.cursor instead + saved := a.cursor + a.cursor.parent = parent + a.cursor.name = name + a.cursor.iter = iter + a.cursor.node = n + + if a.pre != nil && !a.pre(&a.cursor) { + a.cursor = saved + return + } + + // walk children + // (the order of the cases matches the order of the corresponding node types in go/ast) + switch n := n.(type) { + case nil: + // nothing to do + + // Comments and fields + case *ast.Comment: + // nothing to do + + case *ast.CommentGroup: + if n != nil { + a.applyList(n, "List") + } + + case *ast.Field: + a.apply(n, "Doc", nil, n.Doc) + a.applyList(n, "Names") + a.apply(n, "Type", nil, n.Type) + a.apply(n, "Tag", nil, n.Tag) + a.apply(n, "Comment", nil, n.Comment) + + case *ast.FieldList: + a.applyList(n, "List") + + // Expressions + case *ast.BadExpr, *ast.Ident, *ast.BasicLit: + // nothing to do + + case *ast.Ellipsis: + a.apply(n, "Elt", nil, n.Elt) + + case *ast.FuncLit: + a.apply(n, "Type", nil, n.Type) + a.apply(n, "Body", nil, n.Body) + + case *ast.CompositeLit: + a.apply(n, "Type", nil, n.Type) + a.applyList(n, "Elts") + + case *ast.ParenExpr: + a.apply(n, "X", nil, n.X) + + case *ast.SelectorExpr: + a.apply(n, "X", nil, n.X) + a.apply(n, "Sel", nil, n.Sel) + + case *ast.IndexExpr: + a.apply(n, "X", nil, n.X) + a.apply(n, "Index", nil, n.Index) + + case *ast.SliceExpr: + a.apply(n, "X", nil, n.X) + a.apply(n, "Low", nil, n.Low) + a.apply(n, "High", nil, n.High) + a.apply(n, "Max", nil, n.Max) + + case *ast.TypeAssertExpr: + a.apply(n, "X", nil, n.X) + a.apply(n, "Type", nil, n.Type) + + case *ast.CallExpr: + a.apply(n, "Fun", nil, n.Fun) + a.applyList(n, "Args") + + case *ast.StarExpr: + a.apply(n, "X", nil, n.X) + + case *ast.UnaryExpr: + a.apply(n, "X", nil, n.X) + + case *ast.BinaryExpr: + a.apply(n, "X", nil, n.X) + a.apply(n, "Y", nil, n.Y) + + case *ast.KeyValueExpr: + a.apply(n, "Key", nil, n.Key) + a.apply(n, "Value", nil, n.Value) + + // Types + case *ast.ArrayType: + a.apply(n, "Len", nil, n.Len) + a.apply(n, "Elt", nil, n.Elt) + + case *ast.StructType: + a.apply(n, "Fields", nil, n.Fields) + + case *ast.FuncType: + a.apply(n, "Params", nil, n.Params) + a.apply(n, "Results", nil, n.Results) + + case *ast.InterfaceType: + a.apply(n, "Methods", nil, n.Methods) + + case *ast.MapType: + a.apply(n, "Key", nil, n.Key) + a.apply(n, "Value", nil, n.Value) + + case *ast.ChanType: + a.apply(n, "Value", nil, n.Value) + + // Statements + case *ast.BadStmt: + // nothing to do + + case *ast.DeclStmt: + a.apply(n, "Decl", nil, n.Decl) + + case *ast.EmptyStmt: + // nothing to do + + case *ast.LabeledStmt: + a.apply(n, "Label", nil, n.Label) + a.apply(n, "Stmt", nil, n.Stmt) + + case *ast.ExprStmt: + a.apply(n, "X", nil, n.X) + + case *ast.SendStmt: + a.apply(n, "Chan", nil, n.Chan) + a.apply(n, "Value", nil, n.Value) + + case *ast.IncDecStmt: + a.apply(n, "X", nil, n.X) + + case *ast.AssignStmt: + a.applyList(n, "Lhs") + a.applyList(n, "Rhs") + + case *ast.GoStmt: + a.apply(n, "Call", nil, n.Call) + + case *ast.DeferStmt: + a.apply(n, "Call", nil, n.Call) + + case *ast.ReturnStmt: + a.applyList(n, "Results") + + case *ast.BranchStmt: + a.apply(n, "Label", nil, n.Label) + + case *ast.BlockStmt: + a.applyList(n, "List") + + case *ast.IfStmt: + a.apply(n, "Init", nil, n.Init) + a.apply(n, "Cond", nil, n.Cond) + a.apply(n, "Body", nil, n.Body) + a.apply(n, "Else", nil, n.Else) + + case *ast.CaseClause: + a.applyList(n, "List") + a.applyList(n, "Body") + + case *ast.SwitchStmt: + a.apply(n, "Init", nil, n.Init) + a.apply(n, "Tag", nil, n.Tag) + a.apply(n, "Body", nil, n.Body) + + case *ast.TypeSwitchStmt: + a.apply(n, "Init", nil, n.Init) + a.apply(n, "Assign", nil, n.Assign) + a.apply(n, "Body", nil, n.Body) + + case *ast.CommClause: + a.apply(n, "Comm", nil, n.Comm) + a.applyList(n, "Body") + + case *ast.SelectStmt: + a.apply(n, "Body", nil, n.Body) + + case *ast.ForStmt: + a.apply(n, "Init", nil, n.Init) + a.apply(n, "Cond", nil, n.Cond) + a.apply(n, "Post", nil, n.Post) + a.apply(n, "Body", nil, n.Body) + + case *ast.RangeStmt: + a.apply(n, "Key", nil, n.Key) + a.apply(n, "Value", nil, n.Value) + a.apply(n, "X", nil, n.X) + a.apply(n, "Body", nil, n.Body) + + // Declarations + case *ast.ImportSpec: + a.apply(n, "Doc", nil, n.Doc) + a.apply(n, "Name", nil, n.Name) + a.apply(n, "Path", nil, n.Path) + a.apply(n, "Comment", nil, n.Comment) + + case *ast.ValueSpec: + a.apply(n, "Doc", nil, n.Doc) + a.applyList(n, "Names") + a.apply(n, "Type", nil, n.Type) + a.applyList(n, "Values") + a.apply(n, "Comment", nil, n.Comment) + + case *ast.TypeSpec: + a.apply(n, "Doc", nil, n.Doc) + a.apply(n, "Name", nil, n.Name) + a.apply(n, "Type", nil, n.Type) + a.apply(n, "Comment", nil, n.Comment) + + case *ast.BadDecl: + // nothing to do + + case *ast.GenDecl: + a.apply(n, "Doc", nil, n.Doc) + a.applyList(n, "Specs") + + case *ast.FuncDecl: + a.apply(n, "Doc", nil, n.Doc) + a.apply(n, "Recv", nil, n.Recv) + a.apply(n, "Name", nil, n.Name) + a.apply(n, "Type", nil, n.Type) + a.apply(n, "Body", nil, n.Body) + + // Files and packages + case *ast.File: + a.apply(n, "Doc", nil, n.Doc) + a.apply(n, "Name", nil, n.Name) + a.applyList(n, "Decls") + // Don't walk n.Comments; they have either been walked already if + // they are Doc comments, or they can be easily walked explicitly. + + case *ast.Package: + // collect and sort names for reproducible behavior + var names []string + for name := range n.Files { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + a.apply(n, name, nil, n.Files[name]) + } + + default: + panic(fmt.Sprintf("Apply: unexpected node type %T", n)) + } + + if a.post != nil && !a.post(&a.cursor) { + panic(abort) + } + + a.cursor = saved +} + +// An iterator controls iteration over a slice of nodes. +type iterator struct { + index, step int +} + +func (a *application) applyList(parent ast.Node, name string) { + // avoid heap-allocating a new iterator for each applyList call; reuse a.iter instead + saved := a.iter + a.iter.index = 0 + for { + // must reload parent.name each time, since cursor modifications might change it + v := reflect.Indirect(reflect.ValueOf(parent)).FieldByName(name) + if a.iter.index >= v.Len() { + break + } + + // element x may be nil in a bad AST - be cautious + var x ast.Node + if e := v.Index(a.iter.index); e.IsValid() { + x = e.Interface().(ast.Node) + } + + a.iter.step = 1 + a.apply(parent, name, &a.iter, x) + a.iter.index += a.iter.step + } + a.iter = saved +} diff --git a/vendor/golang.org/x/tools/go/ast/astutil/util.go b/vendor/golang.org/x/tools/go/ast/astutil/util.go new file mode 100644 index 0000000000..7630629824 --- /dev/null +++ b/vendor/golang.org/x/tools/go/ast/astutil/util.go @@ -0,0 +1,14 @@ +package astutil + +import "go/ast" + +// Unparen returns e with any enclosing parentheses stripped. +func Unparen(e ast.Expr) ast.Expr { + for { + p, ok := e.(*ast.ParenExpr) + if !ok { + return e + } + e = p.X + } +} diff --git a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go new file mode 100644 index 0000000000..98b3987b97 --- /dev/null +++ b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go @@ -0,0 +1,109 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gcexportdata provides functions for locating, reading, and +// writing export data files containing type information produced by the +// gc compiler. This package supports go1.7 export data format and all +// later versions. +// +// Although it might seem convenient for this package to live alongside +// go/types in the standard library, this would cause version skew +// problems for developer tools that use it, since they must be able to +// consume the outputs of the gc compiler both before and after a Go +// update such as from Go 1.7 to Go 1.8. Because this package lives in +// golang.org/x/tools, sites can update their version of this repo some +// time before the Go 1.8 release and rebuild and redeploy their +// developer tools, which will then be able to consume both Go 1.7 and +// Go 1.8 export data files, so they will work before and after the +// Go update. (See discussion at https://golang.org/issue/15651.) +// +package gcexportdata // import "golang.org/x/tools/go/gcexportdata" + +import ( + "bufio" + "bytes" + "fmt" + "go/token" + "go/types" + "io" + "io/ioutil" + + "golang.org/x/tools/go/internal/gcimporter" +) + +// Find returns the name of an object (.o) or archive (.a) file +// containing type information for the specified import path, +// using the workspace layout conventions of go/build. +// If no file was found, an empty filename is returned. +// +// A relative srcDir is interpreted relative to the current working directory. +// +// Find also returns the package's resolved (canonical) import path, +// reflecting the effects of srcDir and vendoring on importPath. +func Find(importPath, srcDir string) (filename, path string) { + return gcimporter.FindPkg(importPath, srcDir) +} + +// NewReader returns a reader for the export data section of an object +// (.o) or archive (.a) file read from r. The new reader may provide +// additional trailing data beyond the end of the export data. +func NewReader(r io.Reader) (io.Reader, error) { + buf := bufio.NewReader(r) + _, err := gcimporter.FindExportData(buf) + // If we ever switch to a zip-like archive format with the ToC + // at the end, we can return the correct portion of export data, + // but for now we must return the entire rest of the file. + return buf, err +} + +// Read reads export data from in, decodes it, and returns type +// information for the package. +// The package name is specified by path. +// File position information is added to fset. +// +// Read may inspect and add to the imports map to ensure that references +// within the export data to other packages are consistent. The caller +// must ensure that imports[path] does not exist, or exists but is +// incomplete (see types.Package.Complete), and Read inserts the +// resulting package into this map entry. +// +// On return, the state of the reader is undefined. +func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) { + data, err := ioutil.ReadAll(in) + if err != nil { + return nil, fmt.Errorf("reading export data for %q: %v", path, err) + } + + if bytes.HasPrefix(data, []byte("!")) { + return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path) + } + + // The App Engine Go runtime v1.6 uses the old export data format. + // TODO(adonovan): delete once v1.7 has been around for a while. + if bytes.HasPrefix(data, []byte("package ")) { + return gcimporter.ImportData(imports, path, path, bytes.NewReader(data)) + } + + // The indexed export format starts with an 'i'; the older + // binary export format starts with a 'c', 'd', or 'v' + // (from "version"). Select appropriate importer. + if len(data) > 0 && data[0] == 'i' { + _, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path) + return pkg, err + } + + _, pkg, err := gcimporter.BImportData(fset, imports, data, path) + return pkg, err +} + +// Write writes encoded type information for the specified package to out. +// The FileSet provides file position information for named objects. +func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error { + b, err := gcimporter.BExportData(fset, pkg) + if err != nil { + return err + } + _, err = out.Write(b) + return err +} diff --git a/vendor/golang.org/x/tools/go/gcexportdata/importer.go b/vendor/golang.org/x/tools/go/gcexportdata/importer.go new file mode 100644 index 0000000000..efe221e7e1 --- /dev/null +++ b/vendor/golang.org/x/tools/go/gcexportdata/importer.go @@ -0,0 +1,73 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gcexportdata + +import ( + "fmt" + "go/token" + "go/types" + "os" +) + +// NewImporter returns a new instance of the types.Importer interface +// that reads type information from export data files written by gc. +// The Importer also satisfies types.ImporterFrom. +// +// Export data files are located using "go build" workspace conventions +// and the build.Default context. +// +// Use this importer instead of go/importer.For("gc", ...) to avoid the +// version-skew problems described in the documentation of this package, +// or to control the FileSet or access the imports map populated during +// package loading. +// +func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom { + return importer{fset, imports} +} + +type importer struct { + fset *token.FileSet + imports map[string]*types.Package +} + +func (imp importer) Import(importPath string) (*types.Package, error) { + return imp.ImportFrom(importPath, "", 0) +} + +func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) { + filename, path := Find(importPath, srcDir) + if filename == "" { + if importPath == "unsafe" { + // Even for unsafe, call Find first in case + // the package was vendored. + return types.Unsafe, nil + } + return nil, fmt.Errorf("can't find import: %s", importPath) + } + + if pkg, ok := imp.imports[path]; ok && pkg.Complete() { + return pkg, nil // cache hit + } + + // open file + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer func() { + f.Close() + if err != nil { + // add file name to error + err = fmt.Errorf("reading export data: %s: %v", filename, err) + } + }() + + r, err := NewReader(f) + if err != nil { + return nil, err + } + + return Read(r, imp.fset, imp.imports, path) +} diff --git a/vendor/golang.org/x/tools/go/gcexportdata/main.go b/vendor/golang.org/x/tools/go/gcexportdata/main.go new file mode 100644 index 0000000000..2713dce64a --- /dev/null +++ b/vendor/golang.org/x/tools/go/gcexportdata/main.go @@ -0,0 +1,99 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// The gcexportdata command is a diagnostic tool that displays the +// contents of gc export data files. +package main + +import ( + "flag" + "fmt" + "go/token" + "go/types" + "log" + "os" + + "golang.org/x/tools/go/gcexportdata" + "golang.org/x/tools/go/types/typeutil" +) + +var packageFlag = flag.String("package", "", "alternative package to print") + +func main() { + log.SetPrefix("gcexportdata: ") + log.SetFlags(0) + flag.Usage = func() { + fmt.Fprintln(os.Stderr, "usage: gcexportdata [-package path] file.a") + } + flag.Parse() + if flag.NArg() != 1 { + flag.Usage() + os.Exit(2) + } + filename := flag.Args()[0] + + f, err := os.Open(filename) + if err != nil { + log.Fatal(err) + } + + r, err := gcexportdata.NewReader(f) + if err != nil { + log.Fatalf("%s: %s", filename, err) + } + + // Decode the package. + const primary = "" + imports := make(map[string]*types.Package) + fset := token.NewFileSet() + pkg, err := gcexportdata.Read(r, fset, imports, primary) + if err != nil { + log.Fatalf("%s: %s", filename, err) + } + + // Optionally select an indirectly mentioned package. + if *packageFlag != "" { + pkg = imports[*packageFlag] + if pkg == nil { + fmt.Fprintf(os.Stderr, "export data file %s does not mention %s; has:\n", + filename, *packageFlag) + for p := range imports { + if p != primary { + fmt.Fprintf(os.Stderr, "\t%s\n", p) + } + } + os.Exit(1) + } + } + + // Print all package-level declarations, including non-exported ones. + fmt.Printf("package %s\n", pkg.Name()) + for _, imp := range pkg.Imports() { + fmt.Printf("import %q\n", imp.Path()) + } + qual := func(p *types.Package) string { + if pkg == p { + return "" + } + return p.Name() + } + scope := pkg.Scope() + for _, name := range scope.Names() { + obj := scope.Lookup(name) + fmt.Printf("%s: %s\n", + fset.Position(obj.Pos()), + types.ObjectString(obj, qual)) + + // For types, print each method. + if _, ok := obj.(*types.TypeName); ok { + for _, method := range typeutil.IntuitiveMethodSet(obj.Type(), nil) { + fmt.Printf("%s: %s\n", + fset.Position(method.Obj().Pos()), + types.SelectionString(method, qual)) + } + } + } +} diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go new file mode 100644 index 0000000000..a807d0aaa2 --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go @@ -0,0 +1,852 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Binary package export. +// This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go; +// see that file for specification of the format. + +package gcimporter + +import ( + "bytes" + "encoding/binary" + "fmt" + "go/ast" + "go/constant" + "go/token" + "go/types" + "math" + "math/big" + "sort" + "strings" +) + +// If debugFormat is set, each integer and string value is preceded by a marker +// and position information in the encoding. This mechanism permits an importer +// to recognize immediately when it is out of sync. The importer recognizes this +// mode automatically (i.e., it can import export data produced with debugging +// support even if debugFormat is not set at the time of import). This mode will +// lead to massively larger export data (by a factor of 2 to 3) and should only +// be enabled during development and debugging. +// +// NOTE: This flag is the first flag to enable if importing dies because of +// (suspected) format errors, and whenever a change is made to the format. +const debugFormat = false // default: false + +// If trace is set, debugging output is printed to std out. +const trace = false // default: false + +// Current export format version. Increase with each format change. +// Note: The latest binary (non-indexed) export format is at version 6. +// This exporter is still at level 4, but it doesn't matter since +// the binary importer can handle older versions just fine. +// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE +// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMEMTED HERE +// 4: type name objects support type aliases, uses aliasTag +// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used) +// 2: removed unused bool in ODCL export (compiler only) +// 1: header format change (more regular), export package for _ struct fields +// 0: Go1.7 encoding +const exportVersion = 4 + +// trackAllTypes enables cycle tracking for all types, not just named +// types. The existing compiler invariants assume that unnamed types +// that are not completely set up are not used, or else there are spurious +// errors. +// If disabled, only named types are tracked, possibly leading to slightly +// less efficient encoding in rare cases. It also prevents the export of +// some corner-case type declarations (but those are not handled correctly +// with with the textual export format either). +// TODO(gri) enable and remove once issues caused by it are fixed +const trackAllTypes = false + +type exporter struct { + fset *token.FileSet + out bytes.Buffer + + // object -> index maps, indexed in order of serialization + strIndex map[string]int + pkgIndex map[*types.Package]int + typIndex map[types.Type]int + + // position encoding + posInfoFormat bool + prevFile string + prevLine int + + // debugging support + written int // bytes written + indent int // for trace +} + +// internalError represents an error generated inside this package. +type internalError string + +func (e internalError) Error() string { return "gcimporter: " + string(e) } + +func internalErrorf(format string, args ...interface{}) error { + return internalError(fmt.Sprintf(format, args...)) +} + +// BExportData returns binary export data for pkg. +// If no file set is provided, position info will be missing. +func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { + defer func() { + if e := recover(); e != nil { + if ierr, ok := e.(internalError); ok { + err = ierr + return + } + // Not an internal error; panic again. + panic(e) + } + }() + + p := exporter{ + fset: fset, + strIndex: map[string]int{"": 0}, // empty string is mapped to 0 + pkgIndex: make(map[*types.Package]int), + typIndex: make(map[types.Type]int), + posInfoFormat: true, // TODO(gri) might become a flag, eventually + } + + // write version info + // The version string must start with "version %d" where %d is the version + // number. Additional debugging information may follow after a blank; that + // text is ignored by the importer. + p.rawStringln(fmt.Sprintf("version %d", exportVersion)) + var debug string + if debugFormat { + debug = "debug" + } + p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly + p.bool(trackAllTypes) + p.bool(p.posInfoFormat) + + // --- generic export data --- + + // populate type map with predeclared "known" types + for index, typ := range predeclared() { + p.typIndex[typ] = index + } + if len(p.typIndex) != len(predeclared()) { + return nil, internalError("duplicate entries in type map?") + } + + // write package data + p.pkg(pkg, true) + if trace { + p.tracef("\n") + } + + // write objects + objcount := 0 + scope := pkg.Scope() + for _, name := range scope.Names() { + if !ast.IsExported(name) { + continue + } + if trace { + p.tracef("\n") + } + p.obj(scope.Lookup(name)) + objcount++ + } + + // indicate end of list + if trace { + p.tracef("\n") + } + p.tag(endTag) + + // for self-verification only (redundant) + p.int(objcount) + + if trace { + p.tracef("\n") + } + + // --- end of export data --- + + return p.out.Bytes(), nil +} + +func (p *exporter) pkg(pkg *types.Package, emptypath bool) { + if pkg == nil { + panic(internalError("unexpected nil pkg")) + } + + // if we saw the package before, write its index (>= 0) + if i, ok := p.pkgIndex[pkg]; ok { + p.index('P', i) + return + } + + // otherwise, remember the package, write the package tag (< 0) and package data + if trace { + p.tracef("P%d = { ", len(p.pkgIndex)) + defer p.tracef("} ") + } + p.pkgIndex[pkg] = len(p.pkgIndex) + + p.tag(packageTag) + p.string(pkg.Name()) + if emptypath { + p.string("") + } else { + p.string(pkg.Path()) + } +} + +func (p *exporter) obj(obj types.Object) { + switch obj := obj.(type) { + case *types.Const: + p.tag(constTag) + p.pos(obj) + p.qualifiedName(obj) + p.typ(obj.Type()) + p.value(obj.Val()) + + case *types.TypeName: + if obj.IsAlias() { + p.tag(aliasTag) + p.pos(obj) + p.qualifiedName(obj) + } else { + p.tag(typeTag) + } + p.typ(obj.Type()) + + case *types.Var: + p.tag(varTag) + p.pos(obj) + p.qualifiedName(obj) + p.typ(obj.Type()) + + case *types.Func: + p.tag(funcTag) + p.pos(obj) + p.qualifiedName(obj) + sig := obj.Type().(*types.Signature) + p.paramList(sig.Params(), sig.Variadic()) + p.paramList(sig.Results(), false) + + default: + panic(internalErrorf("unexpected object %v (%T)", obj, obj)) + } +} + +func (p *exporter) pos(obj types.Object) { + if !p.posInfoFormat { + return + } + + file, line := p.fileLine(obj) + if file == p.prevFile { + // common case: write line delta + // delta == 0 means different file or no line change + delta := line - p.prevLine + p.int(delta) + if delta == 0 { + p.int(-1) // -1 means no file change + } + } else { + // different file + p.int(0) + // Encode filename as length of common prefix with previous + // filename, followed by (possibly empty) suffix. Filenames + // frequently share path prefixes, so this can save a lot + // of space and make export data size less dependent on file + // path length. The suffix is unlikely to be empty because + // file names tend to end in ".go". + n := commonPrefixLen(p.prevFile, file) + p.int(n) // n >= 0 + p.string(file[n:]) // write suffix only + p.prevFile = file + p.int(line) + } + p.prevLine = line +} + +func (p *exporter) fileLine(obj types.Object) (file string, line int) { + if p.fset != nil { + pos := p.fset.Position(obj.Pos()) + file = pos.Filename + line = pos.Line + } + return +} + +func commonPrefixLen(a, b string) int { + if len(a) > len(b) { + a, b = b, a + } + // len(a) <= len(b) + i := 0 + for i < len(a) && a[i] == b[i] { + i++ + } + return i +} + +func (p *exporter) qualifiedName(obj types.Object) { + p.string(obj.Name()) + p.pkg(obj.Pkg(), false) +} + +func (p *exporter) typ(t types.Type) { + if t == nil { + panic(internalError("nil type")) + } + + // Possible optimization: Anonymous pointer types *T where + // T is a named type are common. We could canonicalize all + // such types *T to a single type PT = *T. This would lead + // to at most one *T entry in typIndex, and all future *T's + // would be encoded as the respective index directly. Would + // save 1 byte (pointerTag) per *T and reduce the typIndex + // size (at the cost of a canonicalization map). We can do + // this later, without encoding format change. + + // if we saw the type before, write its index (>= 0) + if i, ok := p.typIndex[t]; ok { + p.index('T', i) + return + } + + // otherwise, remember the type, write the type tag (< 0) and type data + if trackAllTypes { + if trace { + p.tracef("T%d = {>\n", len(p.typIndex)) + defer p.tracef("<\n} ") + } + p.typIndex[t] = len(p.typIndex) + } + + switch t := t.(type) { + case *types.Named: + if !trackAllTypes { + // if we don't track all types, track named types now + p.typIndex[t] = len(p.typIndex) + } + + p.tag(namedTag) + p.pos(t.Obj()) + p.qualifiedName(t.Obj()) + p.typ(t.Underlying()) + if !types.IsInterface(t) { + p.assocMethods(t) + } + + case *types.Array: + p.tag(arrayTag) + p.int64(t.Len()) + p.typ(t.Elem()) + + case *types.Slice: + p.tag(sliceTag) + p.typ(t.Elem()) + + case *dddSlice: + p.tag(dddTag) + p.typ(t.elem) + + case *types.Struct: + p.tag(structTag) + p.fieldList(t) + + case *types.Pointer: + p.tag(pointerTag) + p.typ(t.Elem()) + + case *types.Signature: + p.tag(signatureTag) + p.paramList(t.Params(), t.Variadic()) + p.paramList(t.Results(), false) + + case *types.Interface: + p.tag(interfaceTag) + p.iface(t) + + case *types.Map: + p.tag(mapTag) + p.typ(t.Key()) + p.typ(t.Elem()) + + case *types.Chan: + p.tag(chanTag) + p.int(int(3 - t.Dir())) // hack + p.typ(t.Elem()) + + default: + panic(internalErrorf("unexpected type %T: %s", t, t)) + } +} + +func (p *exporter) assocMethods(named *types.Named) { + // Sort methods (for determinism). + var methods []*types.Func + for i := 0; i < named.NumMethods(); i++ { + methods = append(methods, named.Method(i)) + } + sort.Sort(methodsByName(methods)) + + p.int(len(methods)) + + if trace && methods != nil { + p.tracef("associated methods {>\n") + } + + for i, m := range methods { + if trace && i > 0 { + p.tracef("\n") + } + + p.pos(m) + name := m.Name() + p.string(name) + if !exported(name) { + p.pkg(m.Pkg(), false) + } + + sig := m.Type().(*types.Signature) + p.paramList(types.NewTuple(sig.Recv()), false) + p.paramList(sig.Params(), sig.Variadic()) + p.paramList(sig.Results(), false) + p.int(0) // dummy value for go:nointerface pragma - ignored by importer + } + + if trace && methods != nil { + p.tracef("<\n} ") + } +} + +type methodsByName []*types.Func + +func (x methodsByName) Len() int { return len(x) } +func (x methodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() } + +func (p *exporter) fieldList(t *types.Struct) { + if trace && t.NumFields() > 0 { + p.tracef("fields {>\n") + defer p.tracef("<\n} ") + } + + p.int(t.NumFields()) + for i := 0; i < t.NumFields(); i++ { + if trace && i > 0 { + p.tracef("\n") + } + p.field(t.Field(i)) + p.string(t.Tag(i)) + } +} + +func (p *exporter) field(f *types.Var) { + if !f.IsField() { + panic(internalError("field expected")) + } + + p.pos(f) + p.fieldName(f) + p.typ(f.Type()) +} + +func (p *exporter) iface(t *types.Interface) { + // TODO(gri): enable importer to load embedded interfaces, + // then emit Embeddeds and ExplicitMethods separately here. + p.int(0) + + n := t.NumMethods() + if trace && n > 0 { + p.tracef("methods {>\n") + defer p.tracef("<\n} ") + } + p.int(n) + for i := 0; i < n; i++ { + if trace && i > 0 { + p.tracef("\n") + } + p.method(t.Method(i)) + } +} + +func (p *exporter) method(m *types.Func) { + sig := m.Type().(*types.Signature) + if sig.Recv() == nil { + panic(internalError("method expected")) + } + + p.pos(m) + p.string(m.Name()) + if m.Name() != "_" && !ast.IsExported(m.Name()) { + p.pkg(m.Pkg(), false) + } + + // interface method; no need to encode receiver. + p.paramList(sig.Params(), sig.Variadic()) + p.paramList(sig.Results(), false) +} + +func (p *exporter) fieldName(f *types.Var) { + name := f.Name() + + if f.Anonymous() { + // anonymous field - we distinguish between 3 cases: + // 1) field name matches base type name and is exported + // 2) field name matches base type name and is not exported + // 3) field name doesn't match base type name (alias name) + bname := basetypeName(f.Type()) + if name == bname { + if ast.IsExported(name) { + name = "" // 1) we don't need to know the field name or package + } else { + name = "?" // 2) use unexported name "?" to force package export + } + } else { + // 3) indicate alias and export name as is + // (this requires an extra "@" but this is a rare case) + p.string("@") + } + } + + p.string(name) + if name != "" && !ast.IsExported(name) { + p.pkg(f.Pkg(), false) + } +} + +func basetypeName(typ types.Type) string { + switch typ := deref(typ).(type) { + case *types.Basic: + return typ.Name() + case *types.Named: + return typ.Obj().Name() + default: + return "" // unnamed type + } +} + +func (p *exporter) paramList(params *types.Tuple, variadic bool) { + // use negative length to indicate unnamed parameters + // (look at the first parameter only since either all + // names are present or all are absent) + n := params.Len() + if n > 0 && params.At(0).Name() == "" { + n = -n + } + p.int(n) + for i := 0; i < params.Len(); i++ { + q := params.At(i) + t := q.Type() + if variadic && i == params.Len()-1 { + t = &dddSlice{t.(*types.Slice).Elem()} + } + p.typ(t) + if n > 0 { + name := q.Name() + p.string(name) + if name != "_" { + p.pkg(q.Pkg(), false) + } + } + p.string("") // no compiler-specific info + } +} + +func (p *exporter) value(x constant.Value) { + if trace { + p.tracef("= ") + } + + switch x.Kind() { + case constant.Bool: + tag := falseTag + if constant.BoolVal(x) { + tag = trueTag + } + p.tag(tag) + + case constant.Int: + if v, exact := constant.Int64Val(x); exact { + // common case: x fits into an int64 - use compact encoding + p.tag(int64Tag) + p.int64(v) + return + } + // uncommon case: large x - use float encoding + // (powers of 2 will be encoded efficiently with exponent) + p.tag(floatTag) + p.float(constant.ToFloat(x)) + + case constant.Float: + p.tag(floatTag) + p.float(x) + + case constant.Complex: + p.tag(complexTag) + p.float(constant.Real(x)) + p.float(constant.Imag(x)) + + case constant.String: + p.tag(stringTag) + p.string(constant.StringVal(x)) + + case constant.Unknown: + // package contains type errors + p.tag(unknownTag) + + default: + panic(internalErrorf("unexpected value %v (%T)", x, x)) + } +} + +func (p *exporter) float(x constant.Value) { + if x.Kind() != constant.Float { + panic(internalErrorf("unexpected constant %v, want float", x)) + } + // extract sign (there is no -0) + sign := constant.Sign(x) + if sign == 0 { + // x == 0 + p.int(0) + return + } + // x != 0 + + var f big.Float + if v, exact := constant.Float64Val(x); exact { + // float64 + f.SetFloat64(v) + } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { + // TODO(gri): add big.Rat accessor to constant.Value. + r := valueToRat(num) + f.SetRat(r.Quo(r, valueToRat(denom))) + } else { + // Value too large to represent as a fraction => inaccessible. + // TODO(gri): add big.Float accessor to constant.Value. + f.SetFloat64(math.MaxFloat64) // FIXME + } + + // extract exponent such that 0.5 <= m < 1.0 + var m big.Float + exp := f.MantExp(&m) + + // extract mantissa as *big.Int + // - set exponent large enough so mant satisfies mant.IsInt() + // - get *big.Int from mant + m.SetMantExp(&m, int(m.MinPrec())) + mant, acc := m.Int(nil) + if acc != big.Exact { + panic(internalError("internal error")) + } + + p.int(sign) + p.int(exp) + p.string(string(mant.Bytes())) +} + +func valueToRat(x constant.Value) *big.Rat { + // Convert little-endian to big-endian. + // I can't believe this is necessary. + bytes := constant.Bytes(x) + for i := 0; i < len(bytes)/2; i++ { + bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] + } + return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) +} + +func (p *exporter) bool(b bool) bool { + if trace { + p.tracef("[") + defer p.tracef("= %v] ", b) + } + + x := 0 + if b { + x = 1 + } + p.int(x) + return b +} + +// ---------------------------------------------------------------------------- +// Low-level encoders + +func (p *exporter) index(marker byte, index int) { + if index < 0 { + panic(internalError("invalid index < 0")) + } + if debugFormat { + p.marker('t') + } + if trace { + p.tracef("%c%d ", marker, index) + } + p.rawInt64(int64(index)) +} + +func (p *exporter) tag(tag int) { + if tag >= 0 { + panic(internalError("invalid tag >= 0")) + } + if debugFormat { + p.marker('t') + } + if trace { + p.tracef("%s ", tagString[-tag]) + } + p.rawInt64(int64(tag)) +} + +func (p *exporter) int(x int) { + p.int64(int64(x)) +} + +func (p *exporter) int64(x int64) { + if debugFormat { + p.marker('i') + } + if trace { + p.tracef("%d ", x) + } + p.rawInt64(x) +} + +func (p *exporter) string(s string) { + if debugFormat { + p.marker('s') + } + if trace { + p.tracef("%q ", s) + } + // if we saw the string before, write its index (>= 0) + // (the empty string is mapped to 0) + if i, ok := p.strIndex[s]; ok { + p.rawInt64(int64(i)) + return + } + // otherwise, remember string and write its negative length and bytes + p.strIndex[s] = len(p.strIndex) + p.rawInt64(-int64(len(s))) + for i := 0; i < len(s); i++ { + p.rawByte(s[i]) + } +} + +// marker emits a marker byte and position information which makes +// it easy for a reader to detect if it is "out of sync". Used for +// debugFormat format only. +func (p *exporter) marker(m byte) { + p.rawByte(m) + // Enable this for help tracking down the location + // of an incorrect marker when running in debugFormat. + if false && trace { + p.tracef("#%d ", p.written) + } + p.rawInt64(int64(p.written)) +} + +// rawInt64 should only be used by low-level encoders. +func (p *exporter) rawInt64(x int64) { + var tmp [binary.MaxVarintLen64]byte + n := binary.PutVarint(tmp[:], x) + for i := 0; i < n; i++ { + p.rawByte(tmp[i]) + } +} + +// rawStringln should only be used to emit the initial version string. +func (p *exporter) rawStringln(s string) { + for i := 0; i < len(s); i++ { + p.rawByte(s[i]) + } + p.rawByte('\n') +} + +// rawByte is the bottleneck interface to write to p.out. +// rawByte escapes b as follows (any encoding does that +// hides '$'): +// +// '$' => '|' 'S' +// '|' => '|' '|' +// +// Necessary so other tools can find the end of the +// export data by searching for "$$". +// rawByte should only be used by low-level encoders. +func (p *exporter) rawByte(b byte) { + switch b { + case '$': + // write '$' as '|' 'S' + b = 'S' + fallthrough + case '|': + // write '|' as '|' '|' + p.out.WriteByte('|') + p.written++ + } + p.out.WriteByte(b) + p.written++ +} + +// tracef is like fmt.Printf but it rewrites the format string +// to take care of indentation. +func (p *exporter) tracef(format string, args ...interface{}) { + if strings.ContainsAny(format, "<>\n") { + var buf bytes.Buffer + for i := 0; i < len(format); i++ { + // no need to deal with runes + ch := format[i] + switch ch { + case '>': + p.indent++ + continue + case '<': + p.indent-- + continue + } + buf.WriteByte(ch) + if ch == '\n' { + for j := p.indent; j > 0; j-- { + buf.WriteString(". ") + } + } + } + format = buf.String() + } + fmt.Printf(format, args...) +} + +// Debugging support. +// (tagString is only used when tracing is enabled) +var tagString = [...]string{ + // Packages + -packageTag: "package", + + // Types + -namedTag: "named type", + -arrayTag: "array", + -sliceTag: "slice", + -dddTag: "ddd", + -structTag: "struct", + -pointerTag: "pointer", + -signatureTag: "signature", + -interfaceTag: "interface", + -mapTag: "map", + -chanTag: "chan", + + // Values + -falseTag: "false", + -trueTag: "true", + -int64Tag: "int64", + -floatTag: "float", + -fractionTag: "fraction", + -complexTag: "complex", + -stringTag: "string", + -unknownTag: "unknown", + + // Type aliases + -aliasTag: "alias", +} diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go new file mode 100644 index 0000000000..e3c3107825 --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go @@ -0,0 +1,1036 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file is a copy of $GOROOT/src/go/internal/gcimporter/bimport.go. + +package gcimporter + +import ( + "encoding/binary" + "fmt" + "go/constant" + "go/token" + "go/types" + "sort" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf8" +) + +type importer struct { + imports map[string]*types.Package + data []byte + importpath string + buf []byte // for reading strings + version int // export format version + + // object lists + strList []string // in order of appearance + pathList []string // in order of appearance + pkgList []*types.Package // in order of appearance + typList []types.Type // in order of appearance + interfaceList []*types.Interface // for delayed completion only + trackAllTypes bool + + // position encoding + posInfoFormat bool + prevFile string + prevLine int + fake fakeFileSet + + // debugging support + debugFormat bool + read int // bytes read +} + +// BImportData imports a package from the serialized package data +// and returns the number of bytes consumed and a reference to the package. +// If the export data version is not recognized or the format is otherwise +// compromised, an error is returned. +func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { + // catch panics and return them as errors + const currentVersion = 6 + version := -1 // unknown version + defer func() { + if e := recover(); e != nil { + // Return a (possibly nil or incomplete) package unchanged (see #16088). + if version > currentVersion { + err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) + } else { + err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) + } + } + }() + + p := importer{ + imports: imports, + data: data, + importpath: path, + version: version, + strList: []string{""}, // empty string is mapped to 0 + pathList: []string{""}, // empty string is mapped to 0 + fake: fakeFileSet{ + fset: fset, + files: make(map[string]*token.File), + }, + } + + // read version info + var versionstr string + if b := p.rawByte(); b == 'c' || b == 'd' { + // Go1.7 encoding; first byte encodes low-level + // encoding format (compact vs debug). + // For backward-compatibility only (avoid problems with + // old installed packages). Newly compiled packages use + // the extensible format string. + // TODO(gri) Remove this support eventually; after Go1.8. + if b == 'd' { + p.debugFormat = true + } + p.trackAllTypes = p.rawByte() == 'a' + p.posInfoFormat = p.int() != 0 + versionstr = p.string() + if versionstr == "v1" { + version = 0 + } + } else { + // Go1.8 extensible encoding + // read version string and extract version number (ignore anything after the version number) + versionstr = p.rawStringln(b) + if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" { + if v, err := strconv.Atoi(s[1]); err == nil && v > 0 { + version = v + } + } + } + p.version = version + + // read version specific flags - extend as necessary + switch p.version { + // case currentVersion: + // ... + // fallthrough + case currentVersion, 5, 4, 3, 2, 1: + p.debugFormat = p.rawStringln(p.rawByte()) == "debug" + p.trackAllTypes = p.int() != 0 + p.posInfoFormat = p.int() != 0 + case 0: + // Go1.7 encoding format - nothing to do here + default: + errorf("unknown bexport format version %d (%q)", p.version, versionstr) + } + + // --- generic export data --- + + // populate typList with predeclared "known" types + p.typList = append(p.typList, predeclared()...) + + // read package data + pkg = p.pkg() + + // read objects of phase 1 only (see cmd/compile/internal/gc/bexport.go) + objcount := 0 + for { + tag := p.tagOrIndex() + if tag == endTag { + break + } + p.obj(tag) + objcount++ + } + + // self-verification + if count := p.int(); count != objcount { + errorf("got %d objects; want %d", objcount, count) + } + + // ignore compiler-specific import data + + // complete interfaces + // TODO(gri) re-investigate if we still need to do this in a delayed fashion + for _, typ := range p.interfaceList { + typ.Complete() + } + + // record all referenced packages as imports + list := append(([]*types.Package)(nil), p.pkgList[1:]...) + sort.Sort(byPath(list)) + pkg.SetImports(list) + + // package was imported completely and without errors + pkg.MarkComplete() + + return p.read, pkg, nil +} + +func errorf(format string, args ...interface{}) { + panic(fmt.Sprintf(format, args...)) +} + +func (p *importer) pkg() *types.Package { + // if the package was seen before, i is its index (>= 0) + i := p.tagOrIndex() + if i >= 0 { + return p.pkgList[i] + } + + // otherwise, i is the package tag (< 0) + if i != packageTag { + errorf("unexpected package tag %d version %d", i, p.version) + } + + // read package data + name := p.string() + var path string + if p.version >= 5 { + path = p.path() + } else { + path = p.string() + } + if p.version >= 6 { + p.int() // package height; unused by go/types + } + + // we should never see an empty package name + if name == "" { + errorf("empty package name in import") + } + + // an empty path denotes the package we are currently importing; + // it must be the first package we see + if (path == "") != (len(p.pkgList) == 0) { + errorf("package path %q for pkg index %d", path, len(p.pkgList)) + } + + // if the package was imported before, use that one; otherwise create a new one + if path == "" { + path = p.importpath + } + pkg := p.imports[path] + if pkg == nil { + pkg = types.NewPackage(path, name) + p.imports[path] = pkg + } else if pkg.Name() != name { + errorf("conflicting names %s and %s for package %q", pkg.Name(), name, path) + } + p.pkgList = append(p.pkgList, pkg) + + return pkg +} + +// objTag returns the tag value for each object kind. +func objTag(obj types.Object) int { + switch obj.(type) { + case *types.Const: + return constTag + case *types.TypeName: + return typeTag + case *types.Var: + return varTag + case *types.Func: + return funcTag + default: + errorf("unexpected object: %v (%T)", obj, obj) // panics + panic("unreachable") + } +} + +func sameObj(a, b types.Object) bool { + // Because unnamed types are not canonicalized, we cannot simply compare types for + // (pointer) identity. + // Ideally we'd check equality of constant values as well, but this is good enough. + return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type()) +} + +func (p *importer) declare(obj types.Object) { + pkg := obj.Pkg() + if alt := pkg.Scope().Insert(obj); alt != nil { + // This can only trigger if we import a (non-type) object a second time. + // Excluding type aliases, this cannot happen because 1) we only import a package + // once; and b) we ignore compiler-specific export data which may contain + // functions whose inlined function bodies refer to other functions that + // were already imported. + // However, type aliases require reexporting the original type, so we need + // to allow it (see also the comment in cmd/compile/internal/gc/bimport.go, + // method importer.obj, switch case importing functions). + // TODO(gri) review/update this comment once the gc compiler handles type aliases. + if !sameObj(obj, alt) { + errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt) + } + } +} + +func (p *importer) obj(tag int) { + switch tag { + case constTag: + pos := p.pos() + pkg, name := p.qualifiedName() + typ := p.typ(nil, nil) + val := p.value() + p.declare(types.NewConst(pos, pkg, name, typ, val)) + + case aliasTag: + // TODO(gri) verify type alias hookup is correct + pos := p.pos() + pkg, name := p.qualifiedName() + typ := p.typ(nil, nil) + p.declare(types.NewTypeName(pos, pkg, name, typ)) + + case typeTag: + p.typ(nil, nil) + + case varTag: + pos := p.pos() + pkg, name := p.qualifiedName() + typ := p.typ(nil, nil) + p.declare(types.NewVar(pos, pkg, name, typ)) + + case funcTag: + pos := p.pos() + pkg, name := p.qualifiedName() + params, isddd := p.paramList() + result, _ := p.paramList() + sig := types.NewSignature(nil, params, result, isddd) + p.declare(types.NewFunc(pos, pkg, name, sig)) + + default: + errorf("unexpected object tag %d", tag) + } +} + +const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go + +func (p *importer) pos() token.Pos { + if !p.posInfoFormat { + return token.NoPos + } + + file := p.prevFile + line := p.prevLine + delta := p.int() + line += delta + if p.version >= 5 { + if delta == deltaNewFile { + if n := p.int(); n >= 0 { + // file changed + file = p.path() + line = n + } + } + } else { + if delta == 0 { + if n := p.int(); n >= 0 { + // file changed + file = p.prevFile[:n] + p.string() + line = p.int() + } + } + } + p.prevFile = file + p.prevLine = line + + return p.fake.pos(file, line) +} + +// Synthesize a token.Pos +type fakeFileSet struct { + fset *token.FileSet + files map[string]*token.File +} + +func (s *fakeFileSet) pos(file string, line int) token.Pos { + // Since we don't know the set of needed file positions, we + // reserve maxlines positions per file. + const maxlines = 64 * 1024 + f := s.files[file] + if f == nil { + f = s.fset.AddFile(file, -1, maxlines) + s.files[file] = f + // Allocate the fake linebreak indices on first use. + // TODO(adonovan): opt: save ~512KB using a more complex scheme? + fakeLinesOnce.Do(func() { + fakeLines = make([]int, maxlines) + for i := range fakeLines { + fakeLines[i] = i + } + }) + f.SetLines(fakeLines) + } + + if line > maxlines { + line = 1 + } + + // Treat the file as if it contained only newlines + // and column=1: use the line number as the offset. + return f.Pos(line - 1) +} + +var ( + fakeLines []int + fakeLinesOnce sync.Once +) + +func (p *importer) qualifiedName() (pkg *types.Package, name string) { + name = p.string() + pkg = p.pkg() + return +} + +func (p *importer) record(t types.Type) { + p.typList = append(p.typList, t) +} + +// A dddSlice is a types.Type representing ...T parameters. +// It only appears for parameter types and does not escape +// the importer. +type dddSlice struct { + elem types.Type +} + +func (t *dddSlice) Underlying() types.Type { return t } +func (t *dddSlice) String() string { return "..." + t.elem.String() } + +// parent is the package which declared the type; parent == nil means +// the package currently imported. The parent package is needed for +// exported struct fields and interface methods which don't contain +// explicit package information in the export data. +// +// A non-nil tname is used as the "owner" of the result type; i.e., +// the result type is the underlying type of tname. tname is used +// to give interface methods a named receiver type where possible. +func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type { + // if the type was seen before, i is its index (>= 0) + i := p.tagOrIndex() + if i >= 0 { + return p.typList[i] + } + + // otherwise, i is the type tag (< 0) + switch i { + case namedTag: + // read type object + pos := p.pos() + parent, name := p.qualifiedName() + scope := parent.Scope() + obj := scope.Lookup(name) + + // if the object doesn't exist yet, create and insert it + if obj == nil { + obj = types.NewTypeName(pos, parent, name, nil) + scope.Insert(obj) + } + + if _, ok := obj.(*types.TypeName); !ok { + errorf("pkg = %s, name = %s => %s", parent, name, obj) + } + + // associate new named type with obj if it doesn't exist yet + t0 := types.NewNamed(obj.(*types.TypeName), nil, nil) + + // but record the existing type, if any + tname := obj.Type().(*types.Named) // tname is either t0 or the existing type + p.record(tname) + + // read underlying type + t0.SetUnderlying(p.typ(parent, t0)) + + // interfaces don't have associated methods + if types.IsInterface(t0) { + return tname + } + + // read associated methods + for i := p.int(); i > 0; i-- { + // TODO(gri) replace this with something closer to fieldName + pos := p.pos() + name := p.string() + if !exported(name) { + p.pkg() + } + + recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver? + params, isddd := p.paramList() + result, _ := p.paramList() + p.int() // go:nointerface pragma - discarded + + sig := types.NewSignature(recv.At(0), params, result, isddd) + t0.AddMethod(types.NewFunc(pos, parent, name, sig)) + } + + return tname + + case arrayTag: + t := new(types.Array) + if p.trackAllTypes { + p.record(t) + } + + n := p.int64() + *t = *types.NewArray(p.typ(parent, nil), n) + return t + + case sliceTag: + t := new(types.Slice) + if p.trackAllTypes { + p.record(t) + } + + *t = *types.NewSlice(p.typ(parent, nil)) + return t + + case dddTag: + t := new(dddSlice) + if p.trackAllTypes { + p.record(t) + } + + t.elem = p.typ(parent, nil) + return t + + case structTag: + t := new(types.Struct) + if p.trackAllTypes { + p.record(t) + } + + *t = *types.NewStruct(p.fieldList(parent)) + return t + + case pointerTag: + t := new(types.Pointer) + if p.trackAllTypes { + p.record(t) + } + + *t = *types.NewPointer(p.typ(parent, nil)) + return t + + case signatureTag: + t := new(types.Signature) + if p.trackAllTypes { + p.record(t) + } + + params, isddd := p.paramList() + result, _ := p.paramList() + *t = *types.NewSignature(nil, params, result, isddd) + return t + + case interfaceTag: + // Create a dummy entry in the type list. This is safe because we + // cannot expect the interface type to appear in a cycle, as any + // such cycle must contain a named type which would have been + // first defined earlier. + // TODO(gri) Is this still true now that we have type aliases? + // See issue #23225. + n := len(p.typList) + if p.trackAllTypes { + p.record(nil) + } + + var embeddeds []types.Type + for n := p.int(); n > 0; n-- { + p.pos() + embeddeds = append(embeddeds, p.typ(parent, nil)) + } + + t := newInterface(p.methodList(parent, tname), embeddeds) + p.interfaceList = append(p.interfaceList, t) + if p.trackAllTypes { + p.typList[n] = t + } + return t + + case mapTag: + t := new(types.Map) + if p.trackAllTypes { + p.record(t) + } + + key := p.typ(parent, nil) + val := p.typ(parent, nil) + *t = *types.NewMap(key, val) + return t + + case chanTag: + t := new(types.Chan) + if p.trackAllTypes { + p.record(t) + } + + dir := chanDir(p.int()) + val := p.typ(parent, nil) + *t = *types.NewChan(dir, val) + return t + + default: + errorf("unexpected type tag %d", i) // panics + panic("unreachable") + } +} + +func chanDir(d int) types.ChanDir { + // tag values must match the constants in cmd/compile/internal/gc/go.go + switch d { + case 1 /* Crecv */ : + return types.RecvOnly + case 2 /* Csend */ : + return types.SendOnly + case 3 /* Cboth */ : + return types.SendRecv + default: + errorf("unexpected channel dir %d", d) + return 0 + } +} + +func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) { + if n := p.int(); n > 0 { + fields = make([]*types.Var, n) + tags = make([]string, n) + for i := range fields { + fields[i], tags[i] = p.field(parent) + } + } + return +} + +func (p *importer) field(parent *types.Package) (*types.Var, string) { + pos := p.pos() + pkg, name, alias := p.fieldName(parent) + typ := p.typ(parent, nil) + tag := p.string() + + anonymous := false + if name == "" { + // anonymous field - typ must be T or *T and T must be a type name + switch typ := deref(typ).(type) { + case *types.Basic: // basic types are named types + pkg = nil // // objects defined in Universe scope have no package + name = typ.Name() + case *types.Named: + name = typ.Obj().Name() + default: + errorf("named base type expected") + } + anonymous = true + } else if alias { + // anonymous field: we have an explicit name because it's an alias + anonymous = true + } + + return types.NewField(pos, pkg, name, typ, anonymous), tag +} + +func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) { + if n := p.int(); n > 0 { + methods = make([]*types.Func, n) + for i := range methods { + methods[i] = p.method(parent, baseType) + } + } + return +} + +func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func { + pos := p.pos() + pkg, name, _ := p.fieldName(parent) + // If we don't have a baseType, use a nil receiver. + // A receiver using the actual interface type (which + // we don't know yet) will be filled in when we call + // types.Interface.Complete. + var recv *types.Var + if baseType != nil { + recv = types.NewVar(token.NoPos, parent, "", baseType) + } + params, isddd := p.paramList() + result, _ := p.paramList() + sig := types.NewSignature(recv, params, result, isddd) + return types.NewFunc(pos, pkg, name, sig) +} + +func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) { + name = p.string() + pkg = parent + if pkg == nil { + // use the imported package instead + pkg = p.pkgList[0] + } + if p.version == 0 && name == "_" { + // version 0 didn't export a package for _ fields + return + } + switch name { + case "": + // 1) field name matches base type name and is exported: nothing to do + case "?": + // 2) field name matches base type name and is not exported: need package + name = "" + pkg = p.pkg() + case "@": + // 3) field name doesn't match type name (alias) + name = p.string() + alias = true + fallthrough + default: + if !exported(name) { + pkg = p.pkg() + } + } + return +} + +func (p *importer) paramList() (*types.Tuple, bool) { + n := p.int() + if n == 0 { + return nil, false + } + // negative length indicates unnamed parameters + named := true + if n < 0 { + n = -n + named = false + } + // n > 0 + params := make([]*types.Var, n) + isddd := false + for i := range params { + params[i], isddd = p.param(named) + } + return types.NewTuple(params...), isddd +} + +func (p *importer) param(named bool) (*types.Var, bool) { + t := p.typ(nil, nil) + td, isddd := t.(*dddSlice) + if isddd { + t = types.NewSlice(td.elem) + } + + var pkg *types.Package + var name string + if named { + name = p.string() + if name == "" { + errorf("expected named parameter") + } + if name != "_" { + pkg = p.pkg() + } + if i := strings.Index(name, "·"); i > 0 { + name = name[:i] // cut off gc-specific parameter numbering + } + } + + // read and discard compiler-specific info + p.string() + + return types.NewVar(token.NoPos, pkg, name, t), isddd +} + +func exported(name string) bool { + ch, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(ch) +} + +func (p *importer) value() constant.Value { + switch tag := p.tagOrIndex(); tag { + case falseTag: + return constant.MakeBool(false) + case trueTag: + return constant.MakeBool(true) + case int64Tag: + return constant.MakeInt64(p.int64()) + case floatTag: + return p.float() + case complexTag: + re := p.float() + im := p.float() + return constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) + case stringTag: + return constant.MakeString(p.string()) + case unknownTag: + return constant.MakeUnknown() + default: + errorf("unexpected value tag %d", tag) // panics + panic("unreachable") + } +} + +func (p *importer) float() constant.Value { + sign := p.int() + if sign == 0 { + return constant.MakeInt64(0) + } + + exp := p.int() + mant := []byte(p.string()) // big endian + + // remove leading 0's if any + for len(mant) > 0 && mant[0] == 0 { + mant = mant[1:] + } + + // convert to little endian + // TODO(gri) go/constant should have a more direct conversion function + // (e.g., once it supports a big.Float based implementation) + for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 { + mant[i], mant[j] = mant[j], mant[i] + } + + // adjust exponent (constant.MakeFromBytes creates an integer value, + // but mant represents the mantissa bits such that 0.5 <= mant < 1.0) + exp -= len(mant) << 3 + if len(mant) > 0 { + for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 { + exp++ + } + } + + x := constant.MakeFromBytes(mant) + switch { + case exp < 0: + d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp)) + x = constant.BinaryOp(x, token.QUO, d) + case exp > 0: + x = constant.Shift(x, token.SHL, uint(exp)) + } + + if sign < 0 { + x = constant.UnaryOp(token.SUB, x, 0) + } + return x +} + +// ---------------------------------------------------------------------------- +// Low-level decoders + +func (p *importer) tagOrIndex() int { + if p.debugFormat { + p.marker('t') + } + + return int(p.rawInt64()) +} + +func (p *importer) int() int { + x := p.int64() + if int64(int(x)) != x { + errorf("exported integer too large") + } + return int(x) +} + +func (p *importer) int64() int64 { + if p.debugFormat { + p.marker('i') + } + + return p.rawInt64() +} + +func (p *importer) path() string { + if p.debugFormat { + p.marker('p') + } + // if the path was seen before, i is its index (>= 0) + // (the empty string is at index 0) + i := p.rawInt64() + if i >= 0 { + return p.pathList[i] + } + // otherwise, i is the negative path length (< 0) + a := make([]string, -i) + for n := range a { + a[n] = p.string() + } + s := strings.Join(a, "/") + p.pathList = append(p.pathList, s) + return s +} + +func (p *importer) string() string { + if p.debugFormat { + p.marker('s') + } + // if the string was seen before, i is its index (>= 0) + // (the empty string is at index 0) + i := p.rawInt64() + if i >= 0 { + return p.strList[i] + } + // otherwise, i is the negative string length (< 0) + if n := int(-i); n <= cap(p.buf) { + p.buf = p.buf[:n] + } else { + p.buf = make([]byte, n) + } + for i := range p.buf { + p.buf[i] = p.rawByte() + } + s := string(p.buf) + p.strList = append(p.strList, s) + return s +} + +func (p *importer) marker(want byte) { + if got := p.rawByte(); got != want { + errorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read) + } + + pos := p.read + if n := int(p.rawInt64()); n != pos { + errorf("incorrect position: got %d; want %d", n, pos) + } +} + +// rawInt64 should only be used by low-level decoders. +func (p *importer) rawInt64() int64 { + i, err := binary.ReadVarint(p) + if err != nil { + errorf("read error: %v", err) + } + return i +} + +// rawStringln should only be used to read the initial version string. +func (p *importer) rawStringln(b byte) string { + p.buf = p.buf[:0] + for b != '\n' { + p.buf = append(p.buf, b) + b = p.rawByte() + } + return string(p.buf) +} + +// needed for binary.ReadVarint in rawInt64 +func (p *importer) ReadByte() (byte, error) { + return p.rawByte(), nil +} + +// byte is the bottleneck interface for reading p.data. +// It unescapes '|' 'S' to '$' and '|' '|' to '|'. +// rawByte should only be used by low-level decoders. +func (p *importer) rawByte() byte { + b := p.data[0] + r := 1 + if b == '|' { + b = p.data[1] + r = 2 + switch b { + case 'S': + b = '$' + case '|': + // nothing to do + default: + errorf("unexpected escape sequence in export data") + } + } + p.data = p.data[r:] + p.read += r + return b + +} + +// ---------------------------------------------------------------------------- +// Export format + +// Tags. Must be < 0. +const ( + // Objects + packageTag = -(iota + 1) + constTag + typeTag + varTag + funcTag + endTag + + // Types + namedTag + arrayTag + sliceTag + dddTag + structTag + pointerTag + signatureTag + interfaceTag + mapTag + chanTag + + // Values + falseTag + trueTag + int64Tag + floatTag + fractionTag // not used by gc + complexTag + stringTag + nilTag // only used by gc (appears in exported inlined function bodies) + unknownTag // not used by gc (only appears in packages with errors) + + // Type aliases + aliasTag +) + +var predecl []types.Type // initialized lazily + +func predeclared() []types.Type { + if predecl == nil { + // initialize lazily to be sure that all + // elements have been initialized before + predecl = []types.Type{ // basic types + types.Typ[types.Bool], + types.Typ[types.Int], + types.Typ[types.Int8], + types.Typ[types.Int16], + types.Typ[types.Int32], + types.Typ[types.Int64], + types.Typ[types.Uint], + types.Typ[types.Uint8], + types.Typ[types.Uint16], + types.Typ[types.Uint32], + types.Typ[types.Uint64], + types.Typ[types.Uintptr], + types.Typ[types.Float32], + types.Typ[types.Float64], + types.Typ[types.Complex64], + types.Typ[types.Complex128], + types.Typ[types.String], + + // basic type aliases + types.Universe.Lookup("byte").Type(), + types.Universe.Lookup("rune").Type(), + + // error + types.Universe.Lookup("error").Type(), + + // untyped types + types.Typ[types.UntypedBool], + types.Typ[types.UntypedInt], + types.Typ[types.UntypedRune], + types.Typ[types.UntypedFloat], + types.Typ[types.UntypedComplex], + types.Typ[types.UntypedString], + types.Typ[types.UntypedNil], + + // package unsafe + types.Typ[types.UnsafePointer], + + // invalid type + types.Typ[types.Invalid], // only appears in packages with errors + + // used internally by gc; never used by this package or in .a files + anyType{}, + } + } + return predecl +} + +type anyType struct{} + +func (t anyType) Underlying() types.Type { return t } +func (t anyType) String() string { return "any" } diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/exportdata.go b/vendor/golang.org/x/tools/go/internal/gcimporter/exportdata.go new file mode 100644 index 0000000000..f33dc5613e --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/exportdata.go @@ -0,0 +1,93 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file is a copy of $GOROOT/src/go/internal/gcimporter/exportdata.go. + +// This file implements FindExportData. + +package gcimporter + +import ( + "bufio" + "fmt" + "io" + "strconv" + "strings" +) + +func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { + // See $GOROOT/include/ar.h. + hdr := make([]byte, 16+12+6+6+8+10+2) + _, err = io.ReadFull(r, hdr) + if err != nil { + return + } + // leave for debugging + if false { + fmt.Printf("header: %s", hdr) + } + s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10])) + size, err = strconv.Atoi(s) + if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { + err = fmt.Errorf("invalid archive header") + return + } + name = strings.TrimSpace(string(hdr[:16])) + return +} + +// FindExportData positions the reader r at the beginning of the +// export data section of an underlying GC-created object/archive +// file by reading from it. The reader must be positioned at the +// start of the file before calling this function. The hdr result +// is the string before the export data, either "$$" or "$$B". +// +func FindExportData(r *bufio.Reader) (hdr string, err error) { + // Read first line to make sure this is an object file. + line, err := r.ReadSlice('\n') + if err != nil { + err = fmt.Errorf("can't find export data (%v)", err) + return + } + + if string(line) == "!\n" { + // Archive file. Scan to __.PKGDEF. + var name string + if name, _, err = readGopackHeader(r); err != nil { + return + } + + // First entry should be __.PKGDEF. + if name != "__.PKGDEF" { + err = fmt.Errorf("go archive is missing __.PKGDEF") + return + } + + // Read first line of __.PKGDEF data, so that line + // is once again the first line of the input. + if line, err = r.ReadSlice('\n'); err != nil { + err = fmt.Errorf("can't find export data (%v)", err) + return + } + } + + // Now at __.PKGDEF in archive or still at beginning of file. + // Either way, line should begin with "go object ". + if !strings.HasPrefix(string(line), "go object ") { + err = fmt.Errorf("not a Go object file") + return + } + + // Skip over object header to export data. + // Begins after first line starting with $$. + for line[0] != '$' { + if line, err = r.ReadSlice('\n'); err != nil { + err = fmt.Errorf("can't find export data (%v)", err) + return + } + } + hdr = string(line) + + return +} diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go b/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go new file mode 100644 index 0000000000..9cf186605f --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go @@ -0,0 +1,1078 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file is a modified copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go, +// but it also contains the original source-based importer code for Go1.6. +// Once we stop supporting 1.6, we can remove that code. + +// Package gcimporter provides various functions for reading +// gc-generated object files that can be used to implement the +// Importer interface defined by the Go 1.5 standard library package. +package gcimporter // import "golang.org/x/tools/go/internal/gcimporter" + +import ( + "bufio" + "errors" + "fmt" + "go/build" + "go/constant" + "go/token" + "go/types" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "text/scanner" +) + +// debugging/development support +const debug = false + +var pkgExts = [...]string{".a", ".o"} + +// FindPkg returns the filename and unique package id for an import +// path based on package information provided by build.Import (using +// the build.Default build.Context). A relative srcDir is interpreted +// relative to the current working directory. +// If no file was found, an empty filename is returned. +// +func FindPkg(path, srcDir string) (filename, id string) { + if path == "" { + return + } + + var noext string + switch { + default: + // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" + // Don't require the source files to be present. + if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 + srcDir = abs + } + bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) + if bp.PkgObj == "" { + id = path // make sure we have an id to print in error message + return + } + noext = strings.TrimSuffix(bp.PkgObj, ".a") + id = bp.ImportPath + + case build.IsLocalImport(path): + // "./x" -> "/this/directory/x.ext", "/this/directory/x" + noext = filepath.Join(srcDir, path) + id = noext + + case filepath.IsAbs(path): + // for completeness only - go/build.Import + // does not support absolute imports + // "/x" -> "/x.ext", "/x" + noext = path + id = path + } + + if false { // for debugging + if path != id { + fmt.Printf("%s -> %s\n", path, id) + } + } + + // try extensions + for _, ext := range pkgExts { + filename = noext + ext + if f, err := os.Stat(filename); err == nil && !f.IsDir() { + return + } + } + + filename = "" // not found + return +} + +// ImportData imports a package by reading the gc-generated export data, +// adds the corresponding package object to the packages map indexed by id, +// and returns the object. +// +// The packages map must contains all packages already imported. The data +// reader position must be the beginning of the export data section. The +// filename is only used in error messages. +// +// If packages[id] contains the completely imported package, that package +// can be used directly, and there is no need to call this function (but +// there is also no harm but for extra time used). +// +func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) { + // support for parser error handling + defer func() { + switch r := recover().(type) { + case nil: + // nothing to do + case importError: + err = r + default: + panic(r) // internal error + } + }() + + var p parser + p.init(filename, id, data, packages) + pkg = p.parseExport() + + return +} + +// Import imports a gc-generated package given its import path and srcDir, adds +// the corresponding package object to the packages map, and returns the object. +// The packages map must contain all packages already imported. +// +func Import(packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) { + var rc io.ReadCloser + var filename, id string + if lookup != nil { + // With custom lookup specified, assume that caller has + // converted path to a canonical import path for use in the map. + if path == "unsafe" { + return types.Unsafe, nil + } + id = path + + // No need to re-import if the package was imported completely before. + if pkg = packages[id]; pkg != nil && pkg.Complete() { + return + } + f, err := lookup(path) + if err != nil { + return nil, err + } + rc = f + } else { + filename, id = FindPkg(path, srcDir) + if filename == "" { + if path == "unsafe" { + return types.Unsafe, nil + } + return nil, fmt.Errorf("can't find import: %q", id) + } + + // no need to re-import if the package was imported completely before + if pkg = packages[id]; pkg != nil && pkg.Complete() { + return + } + + // open file + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer func() { + if err != nil { + // add file name to error + err = fmt.Errorf("%s: %v", filename, err) + } + }() + rc = f + } + defer rc.Close() + + var hdr string + buf := bufio.NewReader(rc) + if hdr, err = FindExportData(buf); err != nil { + return + } + + switch hdr { + case "$$\n": + // Work-around if we don't have a filename; happens only if lookup != nil. + // Either way, the filename is only needed for importer error messages, so + // this is fine. + if filename == "" { + filename = path + } + return ImportData(packages, filename, id, buf) + + case "$$B\n": + var data []byte + data, err = ioutil.ReadAll(buf) + if err != nil { + break + } + + // TODO(gri): allow clients of go/importer to provide a FileSet. + // Or, define a new standard go/types/gcexportdata package. + fset := token.NewFileSet() + + // The indexed export format starts with an 'i'; the older + // binary export format starts with a 'c', 'd', or 'v' + // (from "version"). Select appropriate importer. + if len(data) > 0 && data[0] == 'i' { + _, pkg, err = IImportData(fset, packages, data[1:], id) + } else { + _, pkg, err = BImportData(fset, packages, data, id) + } + + default: + err = fmt.Errorf("unknown export data header: %q", hdr) + } + + return +} + +// ---------------------------------------------------------------------------- +// Parser + +// TODO(gri) Imported objects don't have position information. +// Ideally use the debug table line info; alternatively +// create some fake position (or the position of the +// import). That way error messages referring to imported +// objects can print meaningful information. + +// parser parses the exports inside a gc compiler-produced +// object/archive file and populates its scope with the results. +type parser struct { + scanner scanner.Scanner + tok rune // current token + lit string // literal string; only valid for Ident, Int, String tokens + id string // package id of imported package + sharedPkgs map[string]*types.Package // package id -> package object (across importer) + localPkgs map[string]*types.Package // package id -> package object (just this package) +} + +func (p *parser) init(filename, id string, src io.Reader, packages map[string]*types.Package) { + p.scanner.Init(src) + p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } + p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments + p.scanner.Whitespace = 1<<'\t' | 1<<' ' + p.scanner.Filename = filename // for good error messages + p.next() + p.id = id + p.sharedPkgs = packages + if debug { + // check consistency of packages map + for _, pkg := range packages { + if pkg.Name() == "" { + fmt.Printf("no package name for %s\n", pkg.Path()) + } + } + } +} + +func (p *parser) next() { + p.tok = p.scanner.Scan() + switch p.tok { + case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·': + p.lit = p.scanner.TokenText() + default: + p.lit = "" + } + if debug { + fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit) + } +} + +func declTypeName(pkg *types.Package, name string) *types.TypeName { + scope := pkg.Scope() + if obj := scope.Lookup(name); obj != nil { + return obj.(*types.TypeName) + } + obj := types.NewTypeName(token.NoPos, pkg, name, nil) + // a named type may be referred to before the underlying type + // is known - set it up + types.NewNamed(obj, nil, nil) + scope.Insert(obj) + return obj +} + +// ---------------------------------------------------------------------------- +// Error handling + +// Internal errors are boxed as importErrors. +type importError struct { + pos scanner.Position + err error +} + +func (e importError) Error() string { + return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) +} + +func (p *parser) error(err interface{}) { + if s, ok := err.(string); ok { + err = errors.New(s) + } + // panic with a runtime.Error if err is not an error + panic(importError{p.scanner.Pos(), err.(error)}) +} + +func (p *parser) errorf(format string, args ...interface{}) { + p.error(fmt.Sprintf(format, args...)) +} + +func (p *parser) expect(tok rune) string { + lit := p.lit + if p.tok != tok { + p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) + } + p.next() + return lit +} + +func (p *parser) expectSpecial(tok string) { + sep := 'x' // not white space + i := 0 + for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' { + sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token + p.next() + i++ + } + if i < len(tok) { + p.errorf("expected %q, got %q", tok, tok[0:i]) + } +} + +func (p *parser) expectKeyword(keyword string) { + lit := p.expect(scanner.Ident) + if lit != keyword { + p.errorf("expected keyword %s, got %q", keyword, lit) + } +} + +// ---------------------------------------------------------------------------- +// Qualified and unqualified names + +// PackageId = string_lit . +// +func (p *parser) parsePackageId() string { + id, err := strconv.Unquote(p.expect(scanner.String)) + if err != nil { + p.error(err) + } + // id == "" stands for the imported package id + // (only known at time of package installation) + if id == "" { + id = p.id + } + return id +} + +// PackageName = ident . +// +func (p *parser) parsePackageName() string { + return p.expect(scanner.Ident) +} + +// dotIdentifier = ( ident | '·' ) { ident | int | '·' } . +func (p *parser) parseDotIdent() string { + ident := "" + if p.tok != scanner.Int { + sep := 'x' // not white space + for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' { + ident += p.lit + sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token + p.next() + } + } + if ident == "" { + p.expect(scanner.Ident) // use expect() for error handling + } + return ident +} + +// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) . +// +func (p *parser) parseQualifiedName() (id, name string) { + p.expect('@') + id = p.parsePackageId() + p.expect('.') + // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields. + if p.tok == '?' { + p.next() + } else { + name = p.parseDotIdent() + } + return +} + +// getPkg returns the package for a given id. If the package is +// not found, create the package and add it to the p.localPkgs +// and p.sharedPkgs maps. name is the (expected) name of the +// package. If name == "", the package name is expected to be +// set later via an import clause in the export data. +// +// id identifies a package, usually by a canonical package path like +// "encoding/json" but possibly by a non-canonical import path like +// "./json". +// +func (p *parser) getPkg(id, name string) *types.Package { + // package unsafe is not in the packages maps - handle explicitly + if id == "unsafe" { + return types.Unsafe + } + + pkg := p.localPkgs[id] + if pkg == nil { + // first import of id from this package + pkg = p.sharedPkgs[id] + if pkg == nil { + // first import of id by this importer; + // add (possibly unnamed) pkg to shared packages + pkg = types.NewPackage(id, name) + p.sharedPkgs[id] = pkg + } + // add (possibly unnamed) pkg to local packages + if p.localPkgs == nil { + p.localPkgs = make(map[string]*types.Package) + } + p.localPkgs[id] = pkg + } else if name != "" { + // package exists already and we have an expected package name; + // make sure names match or set package name if necessary + if pname := pkg.Name(); pname == "" { + pkg.SetName(name) + } else if pname != name { + p.errorf("%s package name mismatch: %s (given) vs %s (expected)", id, pname, name) + } + } + return pkg +} + +// parseExportedName is like parseQualifiedName, but +// the package id is resolved to an imported *types.Package. +// +func (p *parser) parseExportedName() (pkg *types.Package, name string) { + id, name := p.parseQualifiedName() + pkg = p.getPkg(id, "") + return +} + +// ---------------------------------------------------------------------------- +// Types + +// BasicType = identifier . +// +func (p *parser) parseBasicType() types.Type { + id := p.expect(scanner.Ident) + obj := types.Universe.Lookup(id) + if obj, ok := obj.(*types.TypeName); ok { + return obj.Type() + } + p.errorf("not a basic type: %s", id) + return nil +} + +// ArrayType = "[" int_lit "]" Type . +// +func (p *parser) parseArrayType(parent *types.Package) types.Type { + // "[" already consumed and lookahead known not to be "]" + lit := p.expect(scanner.Int) + p.expect(']') + elem := p.parseType(parent) + n, err := strconv.ParseInt(lit, 10, 64) + if err != nil { + p.error(err) + } + return types.NewArray(elem, n) +} + +// MapType = "map" "[" Type "]" Type . +// +func (p *parser) parseMapType(parent *types.Package) types.Type { + p.expectKeyword("map") + p.expect('[') + key := p.parseType(parent) + p.expect(']') + elem := p.parseType(parent) + return types.NewMap(key, elem) +} + +// Name = identifier | "?" | QualifiedName . +// +// For unqualified and anonymous names, the returned package is the parent +// package unless parent == nil, in which case the returned package is the +// package being imported. (The parent package is not nil if the the name +// is an unqualified struct field or interface method name belonging to a +// type declared in another package.) +// +// For qualified names, the returned package is nil (and not created if +// it doesn't exist yet) unless materializePkg is set (which creates an +// unnamed package with valid package path). In the latter case, a +// subsequent import clause is expected to provide a name for the package. +// +func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) { + pkg = parent + if pkg == nil { + pkg = p.sharedPkgs[p.id] + } + switch p.tok { + case scanner.Ident: + name = p.lit + p.next() + case '?': + // anonymous + p.next() + case '@': + // exported name prefixed with package path + pkg = nil + var id string + id, name = p.parseQualifiedName() + if materializePkg { + pkg = p.getPkg(id, "") + } + default: + p.error("name expected") + } + return +} + +func deref(typ types.Type) types.Type { + if p, _ := typ.(*types.Pointer); p != nil { + return p.Elem() + } + return typ +} + +// Field = Name Type [ string_lit ] . +// +func (p *parser) parseField(parent *types.Package) (*types.Var, string) { + pkg, name := p.parseName(parent, true) + + if name == "_" { + // Blank fields should be package-qualified because they + // are unexported identifiers, but gc does not qualify them. + // Assuming that the ident belongs to the current package + // causes types to change during re-exporting, leading + // to spurious "can't assign A to B" errors from go/types. + // As a workaround, pretend all blank fields belong + // to the same unique dummy package. + const blankpkg = "<_>" + pkg = p.getPkg(blankpkg, blankpkg) + } + + typ := p.parseType(parent) + anonymous := false + if name == "" { + // anonymous field - typ must be T or *T and T must be a type name + switch typ := deref(typ).(type) { + case *types.Basic: // basic types are named types + pkg = nil // objects defined in Universe scope have no package + name = typ.Name() + case *types.Named: + name = typ.Obj().Name() + default: + p.errorf("anonymous field expected") + } + anonymous = true + } + tag := "" + if p.tok == scanner.String { + s := p.expect(scanner.String) + var err error + tag, err = strconv.Unquote(s) + if err != nil { + p.errorf("invalid struct tag %s: %s", s, err) + } + } + return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag +} + +// StructType = "struct" "{" [ FieldList ] "}" . +// FieldList = Field { ";" Field } . +// +func (p *parser) parseStructType(parent *types.Package) types.Type { + var fields []*types.Var + var tags []string + + p.expectKeyword("struct") + p.expect('{') + for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ { + if i > 0 { + p.expect(';') + } + fld, tag := p.parseField(parent) + if tag != "" && tags == nil { + tags = make([]string, i) + } + if tags != nil { + tags = append(tags, tag) + } + fields = append(fields, fld) + } + p.expect('}') + + return types.NewStruct(fields, tags) +} + +// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . +// +func (p *parser) parseParameter() (par *types.Var, isVariadic bool) { + _, name := p.parseName(nil, false) + // remove gc-specific parameter numbering + if i := strings.Index(name, "·"); i >= 0 { + name = name[:i] + } + if p.tok == '.' { + p.expectSpecial("...") + isVariadic = true + } + typ := p.parseType(nil) + if isVariadic { + typ = types.NewSlice(typ) + } + // ignore argument tag (e.g. "noescape") + if p.tok == scanner.String { + p.next() + } + // TODO(gri) should we provide a package? + par = types.NewVar(token.NoPos, nil, name, typ) + return +} + +// Parameters = "(" [ ParameterList ] ")" . +// ParameterList = { Parameter "," } Parameter . +// +func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) { + p.expect('(') + for p.tok != ')' && p.tok != scanner.EOF { + if len(list) > 0 { + p.expect(',') + } + par, variadic := p.parseParameter() + list = append(list, par) + if variadic { + if isVariadic { + p.error("... not on final argument") + } + isVariadic = true + } + } + p.expect(')') + + return +} + +// Signature = Parameters [ Result ] . +// Result = Type | Parameters . +// +func (p *parser) parseSignature(recv *types.Var) *types.Signature { + params, isVariadic := p.parseParameters() + + // optional result type + var results []*types.Var + if p.tok == '(' { + var variadic bool + results, variadic = p.parseParameters() + if variadic { + p.error("... not permitted on result type") + } + } + + return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic) +} + +// InterfaceType = "interface" "{" [ MethodList ] "}" . +// MethodList = Method { ";" Method } . +// Method = Name Signature . +// +// The methods of embedded interfaces are always "inlined" +// by the compiler and thus embedded interfaces are never +// visible in the export data. +// +func (p *parser) parseInterfaceType(parent *types.Package) types.Type { + var methods []*types.Func + + p.expectKeyword("interface") + p.expect('{') + for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ { + if i > 0 { + p.expect(';') + } + pkg, name := p.parseName(parent, true) + sig := p.parseSignature(nil) + methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig)) + } + p.expect('}') + + // Complete requires the type's embedded interfaces to be fully defined, + // but we do not define any + return types.NewInterface(methods, nil).Complete() +} + +// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . +// +func (p *parser) parseChanType(parent *types.Package) types.Type { + dir := types.SendRecv + if p.tok == scanner.Ident { + p.expectKeyword("chan") + if p.tok == '<' { + p.expectSpecial("<-") + dir = types.SendOnly + } + } else { + p.expectSpecial("<-") + p.expectKeyword("chan") + dir = types.RecvOnly + } + elem := p.parseType(parent) + return types.NewChan(dir, elem) +} + +// Type = +// BasicType | TypeName | ArrayType | SliceType | StructType | +// PointerType | FuncType | InterfaceType | MapType | ChanType | +// "(" Type ")" . +// +// BasicType = ident . +// TypeName = ExportedName . +// SliceType = "[" "]" Type . +// PointerType = "*" Type . +// FuncType = "func" Signature . +// +func (p *parser) parseType(parent *types.Package) types.Type { + switch p.tok { + case scanner.Ident: + switch p.lit { + default: + return p.parseBasicType() + case "struct": + return p.parseStructType(parent) + case "func": + // FuncType + p.next() + return p.parseSignature(nil) + case "interface": + return p.parseInterfaceType(parent) + case "map": + return p.parseMapType(parent) + case "chan": + return p.parseChanType(parent) + } + case '@': + // TypeName + pkg, name := p.parseExportedName() + return declTypeName(pkg, name).Type() + case '[': + p.next() // look ahead + if p.tok == ']' { + // SliceType + p.next() + return types.NewSlice(p.parseType(parent)) + } + return p.parseArrayType(parent) + case '*': + // PointerType + p.next() + return types.NewPointer(p.parseType(parent)) + case '<': + return p.parseChanType(parent) + case '(': + // "(" Type ")" + p.next() + typ := p.parseType(parent) + p.expect(')') + return typ + } + p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit) + return nil +} + +// ---------------------------------------------------------------------------- +// Declarations + +// ImportDecl = "import" PackageName PackageId . +// +func (p *parser) parseImportDecl() { + p.expectKeyword("import") + name := p.parsePackageName() + p.getPkg(p.parsePackageId(), name) +} + +// int_lit = [ "+" | "-" ] { "0" ... "9" } . +// +func (p *parser) parseInt() string { + s := "" + switch p.tok { + case '-': + s = "-" + p.next() + case '+': + p.next() + } + return s + p.expect(scanner.Int) +} + +// number = int_lit [ "p" int_lit ] . +// +func (p *parser) parseNumber() (typ *types.Basic, val constant.Value) { + // mantissa + mant := constant.MakeFromLiteral(p.parseInt(), token.INT, 0) + if mant == nil { + panic("invalid mantissa") + } + + if p.lit == "p" { + // exponent (base 2) + p.next() + exp, err := strconv.ParseInt(p.parseInt(), 10, 0) + if err != nil { + p.error(err) + } + if exp < 0 { + denom := constant.MakeInt64(1) + denom = constant.Shift(denom, token.SHL, uint(-exp)) + typ = types.Typ[types.UntypedFloat] + val = constant.BinaryOp(mant, token.QUO, denom) + return + } + if exp > 0 { + mant = constant.Shift(mant, token.SHL, uint(exp)) + } + typ = types.Typ[types.UntypedFloat] + val = mant + return + } + + typ = types.Typ[types.UntypedInt] + val = mant + return +} + +// ConstDecl = "const" ExportedName [ Type ] "=" Literal . +// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit . +// bool_lit = "true" | "false" . +// complex_lit = "(" float_lit "+" float_lit "i" ")" . +// rune_lit = "(" int_lit "+" int_lit ")" . +// string_lit = `"` { unicode_char } `"` . +// +func (p *parser) parseConstDecl() { + p.expectKeyword("const") + pkg, name := p.parseExportedName() + + var typ0 types.Type + if p.tok != '=' { + // constant types are never structured - no need for parent type + typ0 = p.parseType(nil) + } + + p.expect('=') + var typ types.Type + var val constant.Value + switch p.tok { + case scanner.Ident: + // bool_lit + if p.lit != "true" && p.lit != "false" { + p.error("expected true or false") + } + typ = types.Typ[types.UntypedBool] + val = constant.MakeBool(p.lit == "true") + p.next() + + case '-', scanner.Int: + // int_lit + typ, val = p.parseNumber() + + case '(': + // complex_lit or rune_lit + p.next() + if p.tok == scanner.Char { + p.next() + p.expect('+') + typ = types.Typ[types.UntypedRune] + _, val = p.parseNumber() + p.expect(')') + break + } + _, re := p.parseNumber() + p.expect('+') + _, im := p.parseNumber() + p.expectKeyword("i") + p.expect(')') + typ = types.Typ[types.UntypedComplex] + val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) + + case scanner.Char: + // rune_lit + typ = types.Typ[types.UntypedRune] + val = constant.MakeFromLiteral(p.lit, token.CHAR, 0) + p.next() + + case scanner.String: + // string_lit + typ = types.Typ[types.UntypedString] + val = constant.MakeFromLiteral(p.lit, token.STRING, 0) + p.next() + + default: + p.errorf("expected literal got %s", scanner.TokenString(p.tok)) + } + + if typ0 == nil { + typ0 = typ + } + + pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val)) +} + +// TypeDecl = "type" ExportedName Type . +// +func (p *parser) parseTypeDecl() { + p.expectKeyword("type") + pkg, name := p.parseExportedName() + obj := declTypeName(pkg, name) + + // The type object may have been imported before and thus already + // have a type associated with it. We still need to parse the type + // structure, but throw it away if the object already has a type. + // This ensures that all imports refer to the same type object for + // a given type declaration. + typ := p.parseType(pkg) + + if name := obj.Type().(*types.Named); name.Underlying() == nil { + name.SetUnderlying(typ) + } +} + +// VarDecl = "var" ExportedName Type . +// +func (p *parser) parseVarDecl() { + p.expectKeyword("var") + pkg, name := p.parseExportedName() + typ := p.parseType(pkg) + pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ)) +} + +// Func = Signature [ Body ] . +// Body = "{" ... "}" . +// +func (p *parser) parseFunc(recv *types.Var) *types.Signature { + sig := p.parseSignature(recv) + if p.tok == '{' { + p.next() + for i := 1; i > 0; p.next() { + switch p.tok { + case '{': + i++ + case '}': + i-- + } + } + } + return sig +} + +// MethodDecl = "func" Receiver Name Func . +// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" . +// +func (p *parser) parseMethodDecl() { + // "func" already consumed + p.expect('(') + recv, _ := p.parseParameter() // receiver + p.expect(')') + + // determine receiver base type object + base := deref(recv.Type()).(*types.Named) + + // parse method name, signature, and possibly inlined body + _, name := p.parseName(nil, false) + sig := p.parseFunc(recv) + + // methods always belong to the same package as the base type object + pkg := base.Obj().Pkg() + + // add method to type unless type was imported before + // and method exists already + // TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small. + base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig)) +} + +// FuncDecl = "func" ExportedName Func . +// +func (p *parser) parseFuncDecl() { + // "func" already consumed + pkg, name := p.parseExportedName() + typ := p.parseFunc(nil) + pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ)) +} + +// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . +// +func (p *parser) parseDecl() { + if p.tok == scanner.Ident { + switch p.lit { + case "import": + p.parseImportDecl() + case "const": + p.parseConstDecl() + case "type": + p.parseTypeDecl() + case "var": + p.parseVarDecl() + case "func": + p.next() // look ahead + if p.tok == '(' { + p.parseMethodDecl() + } else { + p.parseFuncDecl() + } + } + } + p.expect('\n') +} + +// ---------------------------------------------------------------------------- +// Export + +// Export = "PackageClause { Decl } "$$" . +// PackageClause = "package" PackageName [ "safe" ] "\n" . +// +func (p *parser) parseExport() *types.Package { + p.expectKeyword("package") + name := p.parsePackageName() + if p.tok == scanner.Ident && p.lit == "safe" { + // package was compiled with -u option - ignore + p.next() + } + p.expect('\n') + + pkg := p.getPkg(p.id, name) + + for p.tok != '$' && p.tok != scanner.EOF { + p.parseDecl() + } + + if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' { + // don't call next()/expect() since reading past the + // export data may cause scanner errors (e.g. NUL chars) + p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch) + } + + if n := p.scanner.ErrorCount; n != 0 { + p.errorf("expected no scanner errors, got %d", n) + } + + // Record all locally referenced packages as imports. + var imports []*types.Package + for id, pkg2 := range p.localPkgs { + if pkg2.Name() == "" { + p.errorf("%s package has no name", id) + } + if id == p.id { + continue // avoid self-edge + } + imports = append(imports, pkg2) + } + sort.Sort(byPath(imports)) + pkg.SetImports(imports) + + // package was imported completely and without errors + pkg.MarkComplete() + + return pkg +} + +type byPath []*types.Package + +func (a byPath) Len() int { return len(a) } +func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() } diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go new file mode 100644 index 0000000000..be671c79b7 --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go @@ -0,0 +1,723 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Indexed binary package export. +// This file was derived from $GOROOT/src/cmd/compile/internal/gc/iexport.go; +// see that file for specification of the format. + +// +build go1.11 + +package gcimporter + +import ( + "bytes" + "encoding/binary" + "go/ast" + "go/constant" + "go/token" + "go/types" + "io" + "math/big" + "reflect" + "sort" +) + +// Current indexed export format version. Increase with each format change. +// 0: Go1.11 encoding +const iexportVersion = 0 + +// IExportData returns the binary export data for pkg. +// If no file set is provided, position info will be missing. +func IExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { + defer func() { + if e := recover(); e != nil { + if ierr, ok := e.(internalError); ok { + err = ierr + return + } + // Not an internal error; panic again. + panic(e) + } + }() + + p := iexporter{ + out: bytes.NewBuffer(nil), + fset: fset, + allPkgs: map[*types.Package]bool{}, + stringIndex: map[string]uint64{}, + declIndex: map[types.Object]uint64{}, + typIndex: map[types.Type]uint64{}, + } + + for i, pt := range predeclared() { + p.typIndex[pt] = uint64(i) + } + if len(p.typIndex) > predeclReserved { + panic(internalErrorf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved)) + } + + // Initialize work queue with exported declarations. + scope := pkg.Scope() + for _, name := range scope.Names() { + if ast.IsExported(name) { + p.pushDecl(scope.Lookup(name)) + } + } + + // Loop until no more work. + for !p.declTodo.empty() { + p.doDecl(p.declTodo.popHead()) + } + + // Append indices to data0 section. + dataLen := uint64(p.data0.Len()) + w := p.newWriter() + w.writeIndex(p.declIndex, pkg) + w.flush() + + // Assemble header. + var hdr intWriter + hdr.WriteByte('i') + hdr.uint64(iexportVersion) + hdr.uint64(uint64(p.strings.Len())) + hdr.uint64(dataLen) + + // Flush output. + io.Copy(p.out, &hdr) + io.Copy(p.out, &p.strings) + io.Copy(p.out, &p.data0) + + return p.out.Bytes(), nil +} + +// writeIndex writes out an object index. mainIndex indicates whether +// we're writing out the main index, which is also read by +// non-compiler tools and includes a complete package description +// (i.e., name and height). +func (w *exportWriter) writeIndex(index map[types.Object]uint64, localpkg *types.Package) { + // Build a map from packages to objects from that package. + pkgObjs := map[*types.Package][]types.Object{} + + // For the main index, make sure to include every package that + // we reference, even if we're not exporting (or reexporting) + // any symbols from it. + pkgObjs[localpkg] = nil + for pkg := range w.p.allPkgs { + pkgObjs[pkg] = nil + } + + for obj := range index { + pkgObjs[obj.Pkg()] = append(pkgObjs[obj.Pkg()], obj) + } + + var pkgs []*types.Package + for pkg, objs := range pkgObjs { + pkgs = append(pkgs, pkg) + + sort.Slice(objs, func(i, j int) bool { + return objs[i].Name() < objs[j].Name() + }) + } + + sort.Slice(pkgs, func(i, j int) bool { + return pkgs[i].Path() < pkgs[j].Path() + }) + + w.uint64(uint64(len(pkgs))) + for _, pkg := range pkgs { + w.string(pkg.Path()) + w.string(pkg.Name()) + w.uint64(uint64(0)) // package height is not needed for go/types + + objs := pkgObjs[pkg] + w.uint64(uint64(len(objs))) + for _, obj := range objs { + w.string(obj.Name()) + w.uint64(index[obj]) + } + } +} + +type iexporter struct { + fset *token.FileSet + out *bytes.Buffer + + // allPkgs tracks all packages that have been referenced by + // the export data, so we can ensure to include them in the + // main index. + allPkgs map[*types.Package]bool + + declTodo objQueue + + strings intWriter + stringIndex map[string]uint64 + + data0 intWriter + declIndex map[types.Object]uint64 + typIndex map[types.Type]uint64 +} + +// stringOff returns the offset of s within the string section. +// If not already present, it's added to the end. +func (p *iexporter) stringOff(s string) uint64 { + off, ok := p.stringIndex[s] + if !ok { + off = uint64(p.strings.Len()) + p.stringIndex[s] = off + + p.strings.uint64(uint64(len(s))) + p.strings.WriteString(s) + } + return off +} + +// pushDecl adds n to the declaration work queue, if not already present. +func (p *iexporter) pushDecl(obj types.Object) { + // Package unsafe is known to the compiler and predeclared. + assert(obj.Pkg() != types.Unsafe) + + if _, ok := p.declIndex[obj]; ok { + return + } + + p.declIndex[obj] = ^uint64(0) // mark n present in work queue + p.declTodo.pushTail(obj) +} + +// exportWriter handles writing out individual data section chunks. +type exportWriter struct { + p *iexporter + + data intWriter + currPkg *types.Package + prevFile string + prevLine int64 +} + +func (p *iexporter) doDecl(obj types.Object) { + w := p.newWriter() + w.setPkg(obj.Pkg(), false) + + switch obj := obj.(type) { + case *types.Var: + w.tag('V') + w.pos(obj.Pos()) + w.typ(obj.Type(), obj.Pkg()) + + case *types.Func: + sig, _ := obj.Type().(*types.Signature) + if sig.Recv() != nil { + panic(internalErrorf("unexpected method: %v", sig)) + } + w.tag('F') + w.pos(obj.Pos()) + w.signature(sig) + + case *types.Const: + w.tag('C') + w.pos(obj.Pos()) + w.value(obj.Type(), obj.Val()) + + case *types.TypeName: + if obj.IsAlias() { + w.tag('A') + w.pos(obj.Pos()) + w.typ(obj.Type(), obj.Pkg()) + break + } + + // Defined type. + w.tag('T') + w.pos(obj.Pos()) + + underlying := obj.Type().Underlying() + w.typ(underlying, obj.Pkg()) + + t := obj.Type() + if types.IsInterface(t) { + break + } + + named, ok := t.(*types.Named) + if !ok { + panic(internalErrorf("%s is not a defined type", t)) + } + + n := named.NumMethods() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + m := named.Method(i) + w.pos(m.Pos()) + w.string(m.Name()) + sig, _ := m.Type().(*types.Signature) + w.param(sig.Recv()) + w.signature(sig) + } + + default: + panic(internalErrorf("unexpected object: %v", obj)) + } + + p.declIndex[obj] = w.flush() +} + +func (w *exportWriter) tag(tag byte) { + w.data.WriteByte(tag) +} + +func (w *exportWriter) pos(pos token.Pos) { + p := w.p.fset.Position(pos) + file := p.Filename + line := int64(p.Line) + + // When file is the same as the last position (common case), + // we can save a few bytes by delta encoding just the line + // number. + // + // Note: Because data objects may be read out of order (or not + // at all), we can only apply delta encoding within a single + // object. This is handled implicitly by tracking prevFile and + // prevLine as fields of exportWriter. + + if file == w.prevFile { + delta := line - w.prevLine + w.int64(delta) + if delta == deltaNewFile { + w.int64(-1) + } + } else { + w.int64(deltaNewFile) + w.int64(line) // line >= 0 + w.string(file) + w.prevFile = file + } + w.prevLine = line +} + +func (w *exportWriter) pkg(pkg *types.Package) { + // Ensure any referenced packages are declared in the main index. + w.p.allPkgs[pkg] = true + + w.string(pkg.Path()) +} + +func (w *exportWriter) qualifiedIdent(obj types.Object) { + // Ensure any referenced declarations are written out too. + w.p.pushDecl(obj) + + w.string(obj.Name()) + w.pkg(obj.Pkg()) +} + +func (w *exportWriter) typ(t types.Type, pkg *types.Package) { + w.data.uint64(w.p.typOff(t, pkg)) +} + +func (p *iexporter) newWriter() *exportWriter { + return &exportWriter{p: p} +} + +func (w *exportWriter) flush() uint64 { + off := uint64(w.p.data0.Len()) + io.Copy(&w.p.data0, &w.data) + return off +} + +func (p *iexporter) typOff(t types.Type, pkg *types.Package) uint64 { + off, ok := p.typIndex[t] + if !ok { + w := p.newWriter() + w.doTyp(t, pkg) + off = predeclReserved + w.flush() + p.typIndex[t] = off + } + return off +} + +func (w *exportWriter) startType(k itag) { + w.data.uint64(uint64(k)) +} + +func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { + switch t := t.(type) { + case *types.Named: + w.startType(definedType) + w.qualifiedIdent(t.Obj()) + + case *types.Pointer: + w.startType(pointerType) + w.typ(t.Elem(), pkg) + + case *types.Slice: + w.startType(sliceType) + w.typ(t.Elem(), pkg) + + case *types.Array: + w.startType(arrayType) + w.uint64(uint64(t.Len())) + w.typ(t.Elem(), pkg) + + case *types.Chan: + w.startType(chanType) + // 1 RecvOnly; 2 SendOnly; 3 SendRecv + var dir uint64 + switch t.Dir() { + case types.RecvOnly: + dir = 1 + case types.SendOnly: + dir = 2 + case types.SendRecv: + dir = 3 + } + w.uint64(dir) + w.typ(t.Elem(), pkg) + + case *types.Map: + w.startType(mapType) + w.typ(t.Key(), pkg) + w.typ(t.Elem(), pkg) + + case *types.Signature: + w.startType(signatureType) + w.setPkg(pkg, true) + w.signature(t) + + case *types.Struct: + w.startType(structType) + w.setPkg(pkg, true) + + n := t.NumFields() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + f := t.Field(i) + w.pos(f.Pos()) + w.string(f.Name()) + w.typ(f.Type(), pkg) + w.bool(f.Embedded()) + w.string(t.Tag(i)) // note (or tag) + } + + case *types.Interface: + w.startType(interfaceType) + w.setPkg(pkg, true) + + n := t.NumEmbeddeds() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + f := t.Embedded(i) + w.pos(f.Obj().Pos()) + w.typ(f.Obj().Type(), f.Obj().Pkg()) + } + + n = t.NumExplicitMethods() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + m := t.ExplicitMethod(i) + w.pos(m.Pos()) + w.string(m.Name()) + sig, _ := m.Type().(*types.Signature) + w.signature(sig) + } + + default: + panic(internalErrorf("unexpected type: %v, %v", t, reflect.TypeOf(t))) + } +} + +func (w *exportWriter) setPkg(pkg *types.Package, write bool) { + if write { + w.pkg(pkg) + } + + w.currPkg = pkg +} + +func (w *exportWriter) signature(sig *types.Signature) { + w.paramList(sig.Params()) + w.paramList(sig.Results()) + if sig.Params().Len() > 0 { + w.bool(sig.Variadic()) + } +} + +func (w *exportWriter) paramList(tup *types.Tuple) { + n := tup.Len() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + w.param(tup.At(i)) + } +} + +func (w *exportWriter) param(obj types.Object) { + w.pos(obj.Pos()) + w.localIdent(obj) + w.typ(obj.Type(), obj.Pkg()) +} + +func (w *exportWriter) value(typ types.Type, v constant.Value) { + w.typ(typ, nil) + + switch v.Kind() { + case constant.Bool: + w.bool(constant.BoolVal(v)) + case constant.Int: + var i big.Int + if i64, exact := constant.Int64Val(v); exact { + i.SetInt64(i64) + } else if ui64, exact := constant.Uint64Val(v); exact { + i.SetUint64(ui64) + } else { + i.SetString(v.ExactString(), 10) + } + w.mpint(&i, typ) + case constant.Float: + f := constantToFloat(v) + w.mpfloat(f, typ) + case constant.Complex: + w.mpfloat(constantToFloat(constant.Real(v)), typ) + w.mpfloat(constantToFloat(constant.Imag(v)), typ) + case constant.String: + w.string(constant.StringVal(v)) + case constant.Unknown: + // package contains type errors + default: + panic(internalErrorf("unexpected value %v (%T)", v, v)) + } +} + +// constantToFloat converts a constant.Value with kind constant.Float to a +// big.Float. +func constantToFloat(x constant.Value) *big.Float { + assert(x.Kind() == constant.Float) + // Use the same floating-point precision (512) as cmd/compile + // (see Mpprec in cmd/compile/internal/gc/mpfloat.go). + const mpprec = 512 + var f big.Float + f.SetPrec(mpprec) + if v, exact := constant.Float64Val(x); exact { + // float64 + f.SetFloat64(v) + } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { + // TODO(gri): add big.Rat accessor to constant.Value. + n := valueToRat(num) + d := valueToRat(denom) + f.SetRat(n.Quo(n, d)) + } else { + // Value too large to represent as a fraction => inaccessible. + // TODO(gri): add big.Float accessor to constant.Value. + _, ok := f.SetString(x.ExactString()) + assert(ok) + } + return &f +} + +// mpint exports a multi-precision integer. +// +// For unsigned types, small values are written out as a single +// byte. Larger values are written out as a length-prefixed big-endian +// byte string, where the length prefix is encoded as its complement. +// For example, bytes 0, 1, and 2 directly represent the integer +// values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-, +// 2-, and 3-byte big-endian string follow. +// +// Encoding for signed types use the same general approach as for +// unsigned types, except small values use zig-zag encoding and the +// bottom bit of length prefix byte for large values is reserved as a +// sign bit. +// +// The exact boundary between small and large encodings varies +// according to the maximum number of bytes needed to encode a value +// of type typ. As a special case, 8-bit types are always encoded as a +// single byte. +// +// TODO(mdempsky): Is this level of complexity really worthwhile? +func (w *exportWriter) mpint(x *big.Int, typ types.Type) { + basic, ok := typ.Underlying().(*types.Basic) + if !ok { + panic(internalErrorf("unexpected type %v (%T)", typ.Underlying(), typ.Underlying())) + } + + signed, maxBytes := intSize(basic) + + negative := x.Sign() < 0 + if !signed && negative { + panic(internalErrorf("negative unsigned integer; type %v, value %v", typ, x)) + } + + b := x.Bytes() + if len(b) > 0 && b[0] == 0 { + panic(internalErrorf("leading zeros")) + } + if uint(len(b)) > maxBytes { + panic(internalErrorf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)) + } + + maxSmall := 256 - maxBytes + if signed { + maxSmall = 256 - 2*maxBytes + } + if maxBytes == 1 { + maxSmall = 256 + } + + // Check if x can use small value encoding. + if len(b) <= 1 { + var ux uint + if len(b) == 1 { + ux = uint(b[0]) + } + if signed { + ux <<= 1 + if negative { + ux-- + } + } + if ux < maxSmall { + w.data.WriteByte(byte(ux)) + return + } + } + + n := 256 - uint(len(b)) + if signed { + n = 256 - 2*uint(len(b)) + if negative { + n |= 1 + } + } + if n < maxSmall || n >= 256 { + panic(internalErrorf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)) + } + + w.data.WriteByte(byte(n)) + w.data.Write(b) +} + +// mpfloat exports a multi-precision floating point number. +// +// The number's value is decomposed into mantissa × 2**exponent, where +// mantissa is an integer. The value is written out as mantissa (as a +// multi-precision integer) and then the exponent, except exponent is +// omitted if mantissa is zero. +func (w *exportWriter) mpfloat(f *big.Float, typ types.Type) { + if f.IsInf() { + panic("infinite constant") + } + + // Break into f = mant × 2**exp, with 0.5 <= mant < 1. + var mant big.Float + exp := int64(f.MantExp(&mant)) + + // Scale so that mant is an integer. + prec := mant.MinPrec() + mant.SetMantExp(&mant, int(prec)) + exp -= int64(prec) + + manti, acc := mant.Int(nil) + if acc != big.Exact { + panic(internalErrorf("mantissa scaling failed for %f (%s)", f, acc)) + } + w.mpint(manti, typ) + if manti.Sign() != 0 { + w.int64(exp) + } +} + +func (w *exportWriter) bool(b bool) bool { + var x uint64 + if b { + x = 1 + } + w.uint64(x) + return b +} + +func (w *exportWriter) int64(x int64) { w.data.int64(x) } +func (w *exportWriter) uint64(x uint64) { w.data.uint64(x) } +func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) } + +func (w *exportWriter) localIdent(obj types.Object) { + // Anonymous parameters. + if obj == nil { + w.string("") + return + } + + name := obj.Name() + if name == "_" { + w.string("_") + return + } + + w.string(name) +} + +type intWriter struct { + bytes.Buffer +} + +func (w *intWriter) int64(x int64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutVarint(buf[:], x) + w.Write(buf[:n]) +} + +func (w *intWriter) uint64(x uint64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], x) + w.Write(buf[:n]) +} + +func assert(cond bool) { + if !cond { + panic("internal error: assertion failed") + } +} + +// The below is copied from go/src/cmd/compile/internal/gc/syntax.go. + +// objQueue is a FIFO queue of types.Object. The zero value of objQueue is +// a ready-to-use empty queue. +type objQueue struct { + ring []types.Object + head, tail int +} + +// empty returns true if q contains no Nodes. +func (q *objQueue) empty() bool { + return q.head == q.tail +} + +// pushTail appends n to the tail of the queue. +func (q *objQueue) pushTail(obj types.Object) { + if len(q.ring) == 0 { + q.ring = make([]types.Object, 16) + } else if q.head+len(q.ring) == q.tail { + // Grow the ring. + nring := make([]types.Object, len(q.ring)*2) + // Copy the old elements. + part := q.ring[q.head%len(q.ring):] + if q.tail-q.head <= len(part) { + part = part[:q.tail-q.head] + copy(nring, part) + } else { + pos := copy(nring, part) + copy(nring[pos:], q.ring[:q.tail%len(q.ring)]) + } + q.ring, q.head, q.tail = nring, 0, q.tail-q.head + } + + q.ring[q.tail%len(q.ring)] = obj + q.tail++ +} + +// popHead pops a node from the head of the queue. It panics if q is empty. +func (q *objQueue) popHead() types.Object { + if q.empty() { + panic("dequeue empty") + } + obj := q.ring[q.head%len(q.ring)] + q.head++ + return obj +} diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go new file mode 100644 index 0000000000..3cb7ae5b9e --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go @@ -0,0 +1,606 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Indexed package import. +// See cmd/compile/internal/gc/iexport.go for the export data format. + +// This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go. + +package gcimporter + +import ( + "bytes" + "encoding/binary" + "fmt" + "go/constant" + "go/token" + "go/types" + "io" + "sort" +) + +type intReader struct { + *bytes.Reader + path string +} + +func (r *intReader) int64() int64 { + i, err := binary.ReadVarint(r.Reader) + if err != nil { + errorf("import %q: read varint error: %v", r.path, err) + } + return i +} + +func (r *intReader) uint64() uint64 { + i, err := binary.ReadUvarint(r.Reader) + if err != nil { + errorf("import %q: read varint error: %v", r.path, err) + } + return i +} + +const predeclReserved = 32 + +type itag uint64 + +const ( + // Types + definedType itag = iota + pointerType + sliceType + arrayType + chanType + mapType + signatureType + structType + interfaceType +) + +// IImportData imports a package from the serialized package data +// and returns the number of bytes consumed and a reference to the package. +// If the export data version is not recognized or the format is otherwise +// compromised, an error is returned. +func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { + const currentVersion = 0 + version := -1 + defer func() { + if e := recover(); e != nil { + if version > currentVersion { + err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) + } else { + err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) + } + } + }() + + r := &intReader{bytes.NewReader(data), path} + + version = int(r.uint64()) + switch version { + case currentVersion: + default: + errorf("unknown iexport format version %d", version) + } + + sLen := int64(r.uint64()) + dLen := int64(r.uint64()) + + whence, _ := r.Seek(0, io.SeekCurrent) + stringData := data[whence : whence+sLen] + declData := data[whence+sLen : whence+sLen+dLen] + r.Seek(sLen+dLen, io.SeekCurrent) + + p := iimporter{ + ipath: path, + + stringData: stringData, + stringCache: make(map[uint64]string), + pkgCache: make(map[uint64]*types.Package), + + declData: declData, + pkgIndex: make(map[*types.Package]map[string]uint64), + typCache: make(map[uint64]types.Type), + + fake: fakeFileSet{ + fset: fset, + files: make(map[string]*token.File), + }, + } + + for i, pt := range predeclared() { + p.typCache[uint64(i)] = pt + } + + pkgList := make([]*types.Package, r.uint64()) + for i := range pkgList { + pkgPathOff := r.uint64() + pkgPath := p.stringAt(pkgPathOff) + pkgName := p.stringAt(r.uint64()) + _ = r.uint64() // package height; unused by go/types + + if pkgPath == "" { + pkgPath = path + } + pkg := imports[pkgPath] + if pkg == nil { + pkg = types.NewPackage(pkgPath, pkgName) + imports[pkgPath] = pkg + } else if pkg.Name() != pkgName { + errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path) + } + + p.pkgCache[pkgPathOff] = pkg + + nameIndex := make(map[string]uint64) + for nSyms := r.uint64(); nSyms > 0; nSyms-- { + name := p.stringAt(r.uint64()) + nameIndex[name] = r.uint64() + } + + p.pkgIndex[pkg] = nameIndex + pkgList[i] = pkg + } + var localpkg *types.Package + for _, pkg := range pkgList { + if pkg.Path() == path { + localpkg = pkg + } + } + + names := make([]string, 0, len(p.pkgIndex[localpkg])) + for name := range p.pkgIndex[localpkg] { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + p.doDecl(localpkg, name) + } + + for _, typ := range p.interfaceList { + typ.Complete() + } + + // record all referenced packages as imports + list := append(([]*types.Package)(nil), pkgList[1:]...) + sort.Sort(byPath(list)) + localpkg.SetImports(list) + + // package was imported completely and without errors + localpkg.MarkComplete() + + consumed, _ := r.Seek(0, io.SeekCurrent) + return int(consumed), localpkg, nil +} + +type iimporter struct { + ipath string + + stringData []byte + stringCache map[uint64]string + pkgCache map[uint64]*types.Package + + declData []byte + pkgIndex map[*types.Package]map[string]uint64 + typCache map[uint64]types.Type + + fake fakeFileSet + interfaceList []*types.Interface +} + +func (p *iimporter) doDecl(pkg *types.Package, name string) { + // See if we've already imported this declaration. + if obj := pkg.Scope().Lookup(name); obj != nil { + return + } + + off, ok := p.pkgIndex[pkg][name] + if !ok { + errorf("%v.%v not in index", pkg, name) + } + + r := &importReader{p: p, currPkg: pkg} + r.declReader.Reset(p.declData[off:]) + + r.obj(name) +} + +func (p *iimporter) stringAt(off uint64) string { + if s, ok := p.stringCache[off]; ok { + return s + } + + slen, n := binary.Uvarint(p.stringData[off:]) + if n <= 0 { + errorf("varint failed") + } + spos := off + uint64(n) + s := string(p.stringData[spos : spos+slen]) + p.stringCache[off] = s + return s +} + +func (p *iimporter) pkgAt(off uint64) *types.Package { + if pkg, ok := p.pkgCache[off]; ok { + return pkg + } + path := p.stringAt(off) + errorf("missing package %q in %q", path, p.ipath) + return nil +} + +func (p *iimporter) typAt(off uint64, base *types.Named) types.Type { + if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) { + return t + } + + if off < predeclReserved { + errorf("predeclared type missing from cache: %v", off) + } + + r := &importReader{p: p} + r.declReader.Reset(p.declData[off-predeclReserved:]) + t := r.doType(base) + + if base == nil || !isInterface(t) { + p.typCache[off] = t + } + return t +} + +type importReader struct { + p *iimporter + declReader bytes.Reader + currPkg *types.Package + prevFile string + prevLine int64 +} + +func (r *importReader) obj(name string) { + tag := r.byte() + pos := r.pos() + + switch tag { + case 'A': + typ := r.typ() + + r.declare(types.NewTypeName(pos, r.currPkg, name, typ)) + + case 'C': + typ, val := r.value() + + r.declare(types.NewConst(pos, r.currPkg, name, typ, val)) + + case 'F': + sig := r.signature(nil) + + r.declare(types.NewFunc(pos, r.currPkg, name, sig)) + + case 'T': + // Types can be recursive. We need to setup a stub + // declaration before recursing. + obj := types.NewTypeName(pos, r.currPkg, name, nil) + named := types.NewNamed(obj, nil, nil) + r.declare(obj) + + underlying := r.p.typAt(r.uint64(), named).Underlying() + named.SetUnderlying(underlying) + + if !isInterface(underlying) { + for n := r.uint64(); n > 0; n-- { + mpos := r.pos() + mname := r.ident() + recv := r.param() + msig := r.signature(recv) + + named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig)) + } + } + + case 'V': + typ := r.typ() + + r.declare(types.NewVar(pos, r.currPkg, name, typ)) + + default: + errorf("unexpected tag: %v", tag) + } +} + +func (r *importReader) declare(obj types.Object) { + obj.Pkg().Scope().Insert(obj) +} + +func (r *importReader) value() (typ types.Type, val constant.Value) { + typ = r.typ() + + switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType { + case types.IsBoolean: + val = constant.MakeBool(r.bool()) + + case types.IsString: + val = constant.MakeString(r.string()) + + case types.IsInteger: + val = r.mpint(b) + + case types.IsFloat: + val = r.mpfloat(b) + + case types.IsComplex: + re := r.mpfloat(b) + im := r.mpfloat(b) + val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) + + default: + if b.Kind() == types.Invalid { + val = constant.MakeUnknown() + return + } + errorf("unexpected type %v", typ) // panics + panic("unreachable") + } + + return +} + +func intSize(b *types.Basic) (signed bool, maxBytes uint) { + if (b.Info() & types.IsUntyped) != 0 { + return true, 64 + } + + switch b.Kind() { + case types.Float32, types.Complex64: + return true, 3 + case types.Float64, types.Complex128: + return true, 7 + } + + signed = (b.Info() & types.IsUnsigned) == 0 + switch b.Kind() { + case types.Int8, types.Uint8: + maxBytes = 1 + case types.Int16, types.Uint16: + maxBytes = 2 + case types.Int32, types.Uint32: + maxBytes = 4 + default: + maxBytes = 8 + } + + return +} + +func (r *importReader) mpint(b *types.Basic) constant.Value { + signed, maxBytes := intSize(b) + + maxSmall := 256 - maxBytes + if signed { + maxSmall = 256 - 2*maxBytes + } + if maxBytes == 1 { + maxSmall = 256 + } + + n, _ := r.declReader.ReadByte() + if uint(n) < maxSmall { + v := int64(n) + if signed { + v >>= 1 + if n&1 != 0 { + v = ^v + } + } + return constant.MakeInt64(v) + } + + v := -n + if signed { + v = -(n &^ 1) >> 1 + } + if v < 1 || uint(v) > maxBytes { + errorf("weird decoding: %v, %v => %v", n, signed, v) + } + + buf := make([]byte, v) + io.ReadFull(&r.declReader, buf) + + // convert to little endian + // TODO(gri) go/constant should have a more direct conversion function + // (e.g., once it supports a big.Float based implementation) + for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 { + buf[i], buf[j] = buf[j], buf[i] + } + + x := constant.MakeFromBytes(buf) + if signed && n&1 != 0 { + x = constant.UnaryOp(token.SUB, x, 0) + } + return x +} + +func (r *importReader) mpfloat(b *types.Basic) constant.Value { + x := r.mpint(b) + if constant.Sign(x) == 0 { + return x + } + + exp := r.int64() + switch { + case exp > 0: + x = constant.Shift(x, token.SHL, uint(exp)) + case exp < 0: + d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp)) + x = constant.BinaryOp(x, token.QUO, d) + } + return x +} + +func (r *importReader) ident() string { + return r.string() +} + +func (r *importReader) qualifiedIdent() (*types.Package, string) { + name := r.string() + pkg := r.pkg() + return pkg, name +} + +func (r *importReader) pos() token.Pos { + delta := r.int64() + if delta != deltaNewFile { + r.prevLine += delta + } else if l := r.int64(); l == -1 { + r.prevLine += deltaNewFile + } else { + r.prevFile = r.string() + r.prevLine = l + } + + if r.prevFile == "" && r.prevLine == 0 { + return token.NoPos + } + + return r.p.fake.pos(r.prevFile, int(r.prevLine)) +} + +func (r *importReader) typ() types.Type { + return r.p.typAt(r.uint64(), nil) +} + +func isInterface(t types.Type) bool { + _, ok := t.(*types.Interface) + return ok +} + +func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) } +func (r *importReader) string() string { return r.p.stringAt(r.uint64()) } + +func (r *importReader) doType(base *types.Named) types.Type { + switch k := r.kind(); k { + default: + errorf("unexpected kind tag in %q: %v", r.p.ipath, k) + return nil + + case definedType: + pkg, name := r.qualifiedIdent() + r.p.doDecl(pkg, name) + return pkg.Scope().Lookup(name).(*types.TypeName).Type() + case pointerType: + return types.NewPointer(r.typ()) + case sliceType: + return types.NewSlice(r.typ()) + case arrayType: + n := r.uint64() + return types.NewArray(r.typ(), int64(n)) + case chanType: + dir := chanDir(int(r.uint64())) + return types.NewChan(dir, r.typ()) + case mapType: + return types.NewMap(r.typ(), r.typ()) + case signatureType: + r.currPkg = r.pkg() + return r.signature(nil) + + case structType: + r.currPkg = r.pkg() + + fields := make([]*types.Var, r.uint64()) + tags := make([]string, len(fields)) + for i := range fields { + fpos := r.pos() + fname := r.ident() + ftyp := r.typ() + emb := r.bool() + tag := r.string() + + fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb) + tags[i] = tag + } + return types.NewStruct(fields, tags) + + case interfaceType: + r.currPkg = r.pkg() + + embeddeds := make([]types.Type, r.uint64()) + for i := range embeddeds { + _ = r.pos() + embeddeds[i] = r.typ() + } + + methods := make([]*types.Func, r.uint64()) + for i := range methods { + mpos := r.pos() + mname := r.ident() + + // TODO(mdempsky): Matches bimport.go, but I + // don't agree with this. + var recv *types.Var + if base != nil { + recv = types.NewVar(token.NoPos, r.currPkg, "", base) + } + + msig := r.signature(recv) + methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig) + } + + typ := newInterface(methods, embeddeds) + r.p.interfaceList = append(r.p.interfaceList, typ) + return typ + } +} + +func (r *importReader) kind() itag { + return itag(r.uint64()) +} + +func (r *importReader) signature(recv *types.Var) *types.Signature { + params := r.paramList() + results := r.paramList() + variadic := params.Len() > 0 && r.bool() + return types.NewSignature(recv, params, results, variadic) +} + +func (r *importReader) paramList() *types.Tuple { + xs := make([]*types.Var, r.uint64()) + for i := range xs { + xs[i] = r.param() + } + return types.NewTuple(xs...) +} + +func (r *importReader) param() *types.Var { + pos := r.pos() + name := r.ident() + typ := r.typ() + return types.NewParam(pos, r.currPkg, name, typ) +} + +func (r *importReader) bool() bool { + return r.uint64() != 0 +} + +func (r *importReader) int64() int64 { + n, err := binary.ReadVarint(&r.declReader) + if err != nil { + errorf("readVarint: %v", err) + } + return n +} + +func (r *importReader) uint64() uint64 { + n, err := binary.ReadUvarint(&r.declReader) + if err != nil { + errorf("readUvarint: %v", err) + } + return n +} + +func (r *importReader) byte() byte { + x, err := r.declReader.ReadByte() + if err != nil { + errorf("declReader.ReadByte: %v", err) + } + return x +} diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go b/vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go new file mode 100644 index 0000000000..463f252271 --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.11 + +package gcimporter + +import "go/types" + +func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface { + named := make([]*types.Named, len(embeddeds)) + for i, e := range embeddeds { + var ok bool + named[i], ok = e.(*types.Named) + if !ok { + panic("embedding of non-defined interfaces in interfaces is not supported before Go 1.11") + } + } + return types.NewInterface(methods, named) +} diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go b/vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go new file mode 100644 index 0000000000..ab28b95cbb --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.11 + +package gcimporter + +import "go/types" + +func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface { + return types.NewInterfaceType(methods, embeddeds) +} diff --git a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go new file mode 100644 index 0000000000..fdc7da0568 --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go @@ -0,0 +1,160 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package packagesdriver fetches type sizes for go/packages and go/analysis. +package packagesdriver + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "go/types" + "log" + "os" + "os/exec" + "strings" + "time" +) + +var debug = false + +// GetSizes returns the sizes used by the underlying driver with the given parameters. +func GetSizes(ctx context.Context, buildFlags, env []string, dir string, usesExportData bool) (types.Sizes, error) { + // TODO(matloob): Clean this up. This code is mostly a copy of packages.findExternalDriver. + const toolPrefix = "GOPACKAGESDRIVER=" + tool := "" + for _, env := range env { + if val := strings.TrimPrefix(env, toolPrefix); val != env { + tool = val + } + } + + if tool == "" { + var err error + tool, err = exec.LookPath("gopackagesdriver") + if err != nil { + // We did not find the driver, so use "go list". + tool = "off" + } + } + + if tool == "off" { + return GetSizesGolist(ctx, buildFlags, env, dir, usesExportData) + } + + req, err := json.Marshal(struct { + Command string `json:"command"` + Env []string `json:"env"` + BuildFlags []string `json:"build_flags"` + }{ + Command: "sizes", + Env: env, + BuildFlags: buildFlags, + }) + if err != nil { + return nil, fmt.Errorf("failed to encode message to driver tool: %v", err) + } + + buf := new(bytes.Buffer) + cmd := exec.CommandContext(ctx, tool) + cmd.Dir = dir + cmd.Env = env + cmd.Stdin = bytes.NewReader(req) + cmd.Stdout = buf + cmd.Stderr = new(bytes.Buffer) + if err := cmd.Run(); err != nil { + return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) + } + var response struct { + // Sizes, if not nil, is the types.Sizes to use when type checking. + Sizes *types.StdSizes + } + if err := json.Unmarshal(buf.Bytes(), &response); err != nil { + return nil, err + } + return response.Sizes, nil +} + +func GetSizesGolist(ctx context.Context, buildFlags, env []string, dir string, usesExportData bool) (types.Sizes, error) { + args := []string{"list", "-f", "{{context.GOARCH}} {{context.Compiler}}"} + args = append(args, buildFlags...) + args = append(args, "--", "unsafe") + stdout, err := InvokeGo(ctx, env, dir, usesExportData, args...) + if err != nil { + return nil, err + } + fields := strings.Fields(stdout.String()) + if len(fields) < 2 { + return nil, fmt.Errorf("could not determine GOARCH and Go compiler") + } + goarch := fields[0] + compiler := fields[1] + return types.SizesFor(compiler, goarch), nil +} + +// InvokeGo returns the stdout of a go command invocation. +func InvokeGo(ctx context.Context, env []string, dir string, usesExportData bool, args ...string) (*bytes.Buffer, error) { + if debug { + defer func(start time.Time) { log.Printf("%s for %v", time.Since(start), cmdDebugStr(env, args...)) }(time.Now()) + } + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + cmd := exec.CommandContext(ctx, "go", args...) + // On darwin the cwd gets resolved to the real path, which breaks anything that + // expects the working directory to keep the original path, including the + // go command when dealing with modules. + // The Go stdlib has a special feature where if the cwd and the PWD are the + // same node then it trusts the PWD, so by setting it in the env for the child + // process we fix up all the paths returned by the go command. + cmd.Env = append(append([]string{}, env...), "PWD="+dir) + cmd.Dir = dir + cmd.Stdout = stdout + cmd.Stderr = stderr + if err := cmd.Run(); err != nil { + exitErr, ok := err.(*exec.ExitError) + if !ok { + // Catastrophic error: + // - executable not found + // - context cancellation + return nil, fmt.Errorf("couldn't exec 'go %v': %s %T", args, err, err) + } + + // Export mode entails a build. + // If that build fails, errors appear on stderr + // (despite the -e flag) and the Export field is blank. + // Do not fail in that case. + if !usesExportData { + return nil, fmt.Errorf("go %v: %s: %s", args, exitErr, stderr) + } + } + + // As of writing, go list -export prints some non-fatal compilation + // errors to stderr, even with -e set. We would prefer that it put + // them in the Package.Error JSON (see https://golang.org/issue/26319). + // In the meantime, there's nowhere good to put them, but they can + // be useful for debugging. Print them if $GOPACKAGESPRINTGOLISTERRORS + // is set. + if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTGOLISTERRORS") != "" { + fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(env, args...), stderr) + } + + // debugging + if false { + fmt.Fprintf(os.Stderr, "%s stdout: <<%s>>\n", cmdDebugStr(env, args...), stdout) + } + + return stdout, nil +} + +func cmdDebugStr(envlist []string, args ...string) string { + env := make(map[string]string) + for _, kv := range envlist { + split := strings.Split(kv, "=") + k, v := split[0], split[1] + env[k] = v + } + + return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v PWD=%v go %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["PWD"], args) +} diff --git a/vendor/golang.org/x/tools/go/packages/doc.go b/vendor/golang.org/x/tools/go/packages/doc.go new file mode 100644 index 0000000000..3799f8ed8b --- /dev/null +++ b/vendor/golang.org/x/tools/go/packages/doc.go @@ -0,0 +1,222 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package packages loads Go packages for inspection and analysis. + +The Load function takes as input a list of patterns and return a list of Package +structs describing individual packages matched by those patterns. +The LoadMode controls the amount of detail in the loaded packages. + +Load passes most patterns directly to the underlying build tool, +but all patterns with the prefix "query=", where query is a +non-empty string of letters from [a-z], are reserved and may be +interpreted as query operators. + +Two query operators are currently supported: "file" and "pattern". + +The query "file=path/to/file.go" matches the package or packages enclosing +the Go source file path/to/file.go. For example "file=~/go/src/fmt/print.go" +might return the packages "fmt" and "fmt [fmt.test]". + +The query "pattern=string" causes "string" to be passed directly to +the underlying build tool. In most cases this is unnecessary, +but an application can use Load("pattern=" + x) as an escaping mechanism +to ensure that x is not interpreted as a query operator if it contains '='. + +All other query operators are reserved for future use and currently +cause Load to report an error. + +The Package struct provides basic information about the package, including + + - ID, a unique identifier for the package in the returned set; + - GoFiles, the names of the package's Go source files; + - Imports, a map from source import strings to the Packages they name; + - Types, the type information for the package's exported symbols; + - Syntax, the parsed syntax trees for the package's source code; and + - TypeInfo, the result of a complete type-check of the package syntax trees. + +(See the documentation for type Package for the complete list of fields +and more detailed descriptions.) + +For example, + + Load(nil, "bytes", "unicode...") + +returns four Package structs describing the standard library packages +bytes, unicode, unicode/utf16, and unicode/utf8. Note that one pattern +can match multiple packages and that a package might be matched by +multiple patterns: in general it is not possible to determine which +packages correspond to which patterns. + +Note that the list returned by Load contains only the packages matched +by the patterns. Their dependencies can be found by walking the import +graph using the Imports fields. + +The Load function can be configured by passing a pointer to a Config as +the first argument. A nil Config is equivalent to the zero Config, which +causes Load to run in LoadFiles mode, collecting minimal information. +See the documentation for type Config for details. + +As noted earlier, the Config.Mode controls the amount of detail +reported about the loaded packages, with each mode returning all the data of the +previous mode with some extra added. See the documentation for type LoadMode +for details. + +Most tools should pass their command-line arguments (after any flags) +uninterpreted to the loader, so that the loader can interpret them +according to the conventions of the underlying build system. +See the Example function for typical usage. + +*/ +package packages // import "golang.org/x/tools/go/packages" + +/* + +Motivation and design considerations + +The new package's design solves problems addressed by two existing +packages: go/build, which locates and describes packages, and +golang.org/x/tools/go/loader, which loads, parses and type-checks them. +The go/build.Package structure encodes too much of the 'go build' way +of organizing projects, leaving us in need of a data type that describes a +package of Go source code independent of the underlying build system. +We wanted something that works equally well with go build and vgo, and +also other build systems such as Bazel and Blaze, making it possible to +construct analysis tools that work in all these environments. +Tools such as errcheck and staticcheck were essentially unavailable to +the Go community at Google, and some of Google's internal tools for Go +are unavailable externally. +This new package provides a uniform way to obtain package metadata by +querying each of these build systems, optionally supporting their +preferred command-line notations for packages, so that tools integrate +neatly with users' build environments. The Metadata query function +executes an external query tool appropriate to the current workspace. + +Loading packages always returns the complete import graph "all the way down", +even if all you want is information about a single package, because the query +mechanisms of all the build systems we currently support ({go,vgo} list, and +blaze/bazel aspect-based query) cannot provide detailed information +about one package without visiting all its dependencies too, so there is +no additional asymptotic cost to providing transitive information. +(This property might not be true of a hypothetical 5th build system.) + +In calls to TypeCheck, all initial packages, and any package that +transitively depends on one of them, must be loaded from source. +Consider A->B->C->D->E: if A,C are initial, A,B,C must be loaded from +source; D may be loaded from export data, and E may not be loaded at all +(though it's possible that D's export data mentions it, so a +types.Package may be created for it and exposed.) + +The old loader had a feature to suppress type-checking of function +bodies on a per-package basis, primarily intended to reduce the work of +obtaining type information for imported packages. Now that imports are +satisfied by export data, the optimization no longer seems necessary. + +Despite some early attempts, the old loader did not exploit export data, +instead always using the equivalent of WholeProgram mode. This was due +to the complexity of mixing source and export data packages (now +resolved by the upward traversal mentioned above), and because export data +files were nearly always missing or stale. Now that 'go build' supports +caching, all the underlying build systems can guarantee to produce +export data in a reasonable (amortized) time. + +Test "main" packages synthesized by the build system are now reported as +first-class packages, avoiding the need for clients (such as go/ssa) to +reinvent this generation logic. + +One way in which go/packages is simpler than the old loader is in its +treatment of in-package tests. In-package tests are packages that +consist of all the files of the library under test, plus the test files. +The old loader constructed in-package tests by a two-phase process of +mutation called "augmentation": first it would construct and type check +all the ordinary library packages and type-check the packages that +depend on them; then it would add more (test) files to the package and +type-check again. This two-phase approach had four major problems: +1) in processing the tests, the loader modified the library package, + leaving no way for a client application to see both the test + package and the library package; one would mutate into the other. +2) because test files can declare additional methods on types defined in + the library portion of the package, the dispatch of method calls in + the library portion was affected by the presence of the test files. + This should have been a clue that the packages were logically + different. +3) this model of "augmentation" assumed at most one in-package test + per library package, which is true of projects using 'go build', + but not other build systems. +4) because of the two-phase nature of test processing, all packages that + import the library package had to be processed before augmentation, + forcing a "one-shot" API and preventing the client from calling Load + in several times in sequence as is now possible in WholeProgram mode. + (TypeCheck mode has a similar one-shot restriction for a different reason.) + +Early drafts of this package supported "multi-shot" operation. +Although it allowed clients to make a sequence of calls (or concurrent +calls) to Load, building up the graph of Packages incrementally, +it was of marginal value: it complicated the API +(since it allowed some options to vary across calls but not others), +it complicated the implementation, +it cannot be made to work in Types mode, as explained above, +and it was less efficient than making one combined call (when this is possible). +Among the clients we have inspected, none made multiple calls to load +but could not be easily and satisfactorily modified to make only a single call. +However, applications changes may be required. +For example, the ssadump command loads the user-specified packages +and in addition the runtime package. It is tempting to simply append +"runtime" to the user-provided list, but that does not work if the user +specified an ad-hoc package such as [a.go b.go]. +Instead, ssadump no longer requests the runtime package, +but seeks it among the dependencies of the user-specified packages, +and emits an error if it is not found. + +Overlays: The Overlay field in the Config allows providing alternate contents +for Go source files, by providing a mapping from file path to contents. +go/packages will pull in new imports added in overlay files when go/packages +is run in LoadImports mode or greater. +Overlay support for the go list driver isn't complete yet: if the file doesn't +exist on disk, it will only be recognized in an overlay if it is a non-test file +and the package would be reported even without the overlay. + +Questions & Tasks + +- Add GOARCH/GOOS? + They are not portable concepts, but could be made portable. + Our goal has been to allow users to express themselves using the conventions + of the underlying build system: if the build system honors GOARCH + during a build and during a metadata query, then so should + applications built atop that query mechanism. + Conversely, if the target architecture of the build is determined by + command-line flags, the application can pass the relevant + flags through to the build system using a command such as: + myapp -query_flag="--cpu=amd64" -query_flag="--os=darwin" + However, this approach is low-level, unwieldy, and non-portable. + GOOS and GOARCH seem important enough to warrant a dedicated option. + +- How should we handle partial failures such as a mixture of good and + malformed patterns, existing and non-existent packages, successful and + failed builds, import failures, import cycles, and so on, in a call to + Load? + +- Support bazel, blaze, and go1.10 list, not just go1.11 list. + +- Handle (and test) various partial success cases, e.g. + a mixture of good packages and: + invalid patterns + nonexistent packages + empty packages + packages with malformed package or import declarations + unreadable files + import cycles + other parse errors + type errors + Make sure we record errors at the correct place in the graph. + +- Missing packages among initial arguments are not reported. + Return bogus packages for them, like golist does. + +- "undeclared name" errors (for example) are reported out of source file + order. I suspect this is due to the breadth-first resolution now used + by go/types. Is that a bug? Discuss with gri. + +*/ diff --git a/vendor/golang.org/x/tools/go/packages/external.go b/vendor/golang.org/x/tools/go/packages/external.go new file mode 100644 index 0000000000..22ff769ef2 --- /dev/null +++ b/vendor/golang.org/x/tools/go/packages/external.go @@ -0,0 +1,79 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file enables an external tool to intercept package requests. +// If the tool is present then its results are used in preference to +// the go list command. + +package packages + +import ( + "bytes" + "encoding/json" + "fmt" + "os/exec" + "strings" +) + +// Driver +type driverRequest struct { + Command string `json:"command"` + Mode LoadMode `json:"mode"` + Env []string `json:"env"` + BuildFlags []string `json:"build_flags"` + Tests bool `json:"tests"` + Overlay map[string][]byte `json:"overlay"` +} + +// findExternalDriver returns the file path of a tool that supplies +// the build system package structure, or "" if not found." +// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its +// value, otherwise it searches for a binary named gopackagesdriver on the PATH. +func findExternalDriver(cfg *Config) driver { + const toolPrefix = "GOPACKAGESDRIVER=" + tool := "" + for _, env := range cfg.Env { + if val := strings.TrimPrefix(env, toolPrefix); val != env { + tool = val + } + } + if tool != "" && tool == "off" { + return nil + } + if tool == "" { + var err error + tool, err = exec.LookPath("gopackagesdriver") + if err != nil { + return nil + } + } + return func(cfg *Config, words ...string) (*driverResponse, error) { + req, err := json.Marshal(driverRequest{ + Mode: cfg.Mode, + Env: cfg.Env, + BuildFlags: cfg.BuildFlags, + Tests: cfg.Tests, + Overlay: cfg.Overlay, + }) + if err != nil { + return nil, fmt.Errorf("failed to encode message to driver tool: %v", err) + } + + buf := new(bytes.Buffer) + cmd := exec.CommandContext(cfg.Context, tool, words...) + cmd.Dir = cfg.Dir + cmd.Env = cfg.Env + cmd.Stdin = bytes.NewReader(req) + cmd.Stdout = buf + cmd.Stderr = new(bytes.Buffer) + if err := cmd.Run(); err != nil { + return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) + } + var response driverResponse + if err := json.Unmarshal(buf.Bytes(), &response); err != nil { + return nil, err + } + return &response, nil + } +} diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go new file mode 100644 index 0000000000..a25c3acb1a --- /dev/null +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -0,0 +1,821 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packages + +import ( + "bytes" + "encoding/json" + "fmt" + "go/types" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "sync" + "time" + + "golang.org/x/tools/go/internal/packagesdriver" + "golang.org/x/tools/internal/gopathwalk" + "golang.org/x/tools/internal/semver" +) + +// debug controls verbose logging. +var debug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG")) + +// A goTooOldError reports that the go command +// found by exec.LookPath is too old to use the new go list behavior. +type goTooOldError struct { + error +} + +// responseDeduper wraps a driverResponse, deduplicating its contents. +type responseDeduper struct { + seenRoots map[string]bool + seenPackages map[string]*Package + dr *driverResponse +} + +// init fills in r with a driverResponse. +func (r *responseDeduper) init(dr *driverResponse) { + r.dr = dr + r.seenRoots = map[string]bool{} + r.seenPackages = map[string]*Package{} + for _, pkg := range dr.Packages { + r.seenPackages[pkg.ID] = pkg + } + for _, root := range dr.Roots { + r.seenRoots[root] = true + } +} + +func (r *responseDeduper) addPackage(p *Package) { + if r.seenPackages[p.ID] != nil { + return + } + r.seenPackages[p.ID] = p + r.dr.Packages = append(r.dr.Packages, p) +} + +func (r *responseDeduper) addRoot(id string) { + if r.seenRoots[id] { + return + } + r.seenRoots[id] = true + r.dr.Roots = append(r.dr.Roots, id) +} + +// goListDriver uses the go list command to interpret the patterns and produce +// the build system package structure. +// See driver for more details. +func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) { + var sizes types.Sizes + var sizeserr error + var sizeswg sync.WaitGroup + if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 { + sizeswg.Add(1) + go func() { + sizes, sizeserr = getSizes(cfg) + sizeswg.Done() + }() + } + + // Determine files requested in contains patterns + var containFiles []string + var packagesNamed []string + restPatterns := make([]string, 0, len(patterns)) + // Extract file= and other [querytype]= patterns. Report an error if querytype + // doesn't exist. +extractQueries: + for _, pattern := range patterns { + eqidx := strings.Index(pattern, "=") + if eqidx < 0 { + restPatterns = append(restPatterns, pattern) + } else { + query, value := pattern[:eqidx], pattern[eqidx+len("="):] + switch query { + case "file": + containFiles = append(containFiles, value) + case "pattern": + restPatterns = append(restPatterns, value) + case "iamashamedtousethedisabledqueryname": + packagesNamed = append(packagesNamed, value) + case "": // not a reserved query + restPatterns = append(restPatterns, pattern) + default: + for _, rune := range query { + if rune < 'a' || rune > 'z' { // not a reserved query + restPatterns = append(restPatterns, pattern) + continue extractQueries + } + } + // Reject all other patterns containing "=" + return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern) + } + } + } + + response := &responseDeduper{} + var err error + + // See if we have any patterns to pass through to go list. Zero initial + // patterns also requires a go list call, since it's the equivalent of + // ".". + if len(restPatterns) > 0 || len(patterns) == 0 { + dr, err := golistDriver(cfg, restPatterns...) + if err != nil { + return nil, err + } + response.init(dr) + } else { + response.init(&driverResponse{}) + } + + sizeswg.Wait() + if sizeserr != nil { + return nil, sizeserr + } + // types.SizesFor always returns nil or a *types.StdSizes + response.dr.Sizes, _ = sizes.(*types.StdSizes) + + var containsCandidates []string + + if len(containFiles) != 0 { + if err := runContainsQueries(cfg, golistDriver, response, containFiles); err != nil { + return nil, err + } + } + + if len(packagesNamed) != 0 { + if err := runNamedQueries(cfg, golistDriver, response, packagesNamed); err != nil { + return nil, err + } + } + + modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response.dr) + if err != nil { + return nil, err + } + if len(containFiles) > 0 { + containsCandidates = append(containsCandidates, modifiedPkgs...) + containsCandidates = append(containsCandidates, needPkgs...) + } + + if len(needPkgs) > 0 { + addNeededOverlayPackages(cfg, golistDriver, response, needPkgs) + if err != nil { + return nil, err + } + } + // Check candidate packages for containFiles. + if len(containFiles) > 0 { + for _, id := range containsCandidates { + pkg := response.seenPackages[id] + for _, f := range containFiles { + for _, g := range pkg.GoFiles { + if sameFile(f, g) { + response.addRoot(id) + } + } + } + } + } + + return response.dr, nil +} + +func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string) error { + dr, err := driver(cfg, pkgs...) + if err != nil { + return err + } + for _, pkg := range dr.Packages { + response.addPackage(pkg) + } + return nil +} + +func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error { + for _, query := range queries { + // TODO(matloob): Do only one query per directory. + fdir := filepath.Dir(query) + // Pass absolute path of directory to go list so that it knows to treat it as a directory, + // not a package path. + pattern, err := filepath.Abs(fdir) + if err != nil { + return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err) + } + dirResponse, err := driver(cfg, pattern) + if err != nil { + return err + } + isRoot := make(map[string]bool, len(dirResponse.Roots)) + for _, root := range dirResponse.Roots { + isRoot[root] = true + } + for _, pkg := range dirResponse.Packages { + // Add any new packages to the main set + // We don't bother to filter packages that will be dropped by the changes of roots, + // that will happen anyway during graph construction outside this function. + // Over-reporting packages is not a problem. + response.addPackage(pkg) + // if the package was not a root one, it cannot have the file + if !isRoot[pkg.ID] { + continue + } + for _, pkgFile := range pkg.GoFiles { + if filepath.Base(query) == filepath.Base(pkgFile) { + response.addRoot(pkg.ID) + break + } + } + } + } + return nil +} + +// modCacheRegexp splits a path in a module cache into module, module version, and package. +var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) + +func runNamedQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error { + // calling `go env` isn't free; bail out if there's nothing to do. + if len(queries) == 0 { + return nil + } + // Determine which directories are relevant to scan. + roots, modRoot, err := roots(cfg) + if err != nil { + return err + } + + // Scan the selected directories. Simple matches, from GOPATH/GOROOT + // or the local module, can simply be "go list"ed. Matches from the + // module cache need special treatment. + var matchesMu sync.Mutex + var simpleMatches, modCacheMatches []string + add := func(root gopathwalk.Root, dir string) { + // Walk calls this concurrently; protect the result slices. + matchesMu.Lock() + defer matchesMu.Unlock() + + path := dir + if dir != root.Path { + path = dir[len(root.Path)+1:] + } + if pathMatchesQueries(path, queries) { + switch root.Type { + case gopathwalk.RootModuleCache: + modCacheMatches = append(modCacheMatches, path) + case gopathwalk.RootCurrentModule: + // We'd need to read go.mod to find the full + // import path. Relative's easier. + rel, err := filepath.Rel(cfg.Dir, dir) + if err != nil { + // This ought to be impossible, since + // we found dir in the current module. + panic(err) + } + simpleMatches = append(simpleMatches, "./"+rel) + case gopathwalk.RootGOPATH, gopathwalk.RootGOROOT: + simpleMatches = append(simpleMatches, path) + } + } + } + + startWalk := time.Now() + gopathwalk.Walk(roots, add, gopathwalk.Options{ModulesEnabled: modRoot != "", Debug: debug}) + if debug { + log.Printf("%v for walk", time.Since(startWalk)) + } + + // Weird special case: the top-level package in a module will be in + // whatever directory the user checked the repository out into. It's + // more reasonable for that to not match the package name. So, if there + // are any Go files in the mod root, query it just to be safe. + if modRoot != "" { + rel, err := filepath.Rel(cfg.Dir, modRoot) + if err != nil { + panic(err) // See above. + } + + files, err := ioutil.ReadDir(modRoot) + for _, f := range files { + if strings.HasSuffix(f.Name(), ".go") { + simpleMatches = append(simpleMatches, rel) + break + } + } + } + + addResponse := func(r *driverResponse) { + for _, pkg := range r.Packages { + response.addPackage(pkg) + for _, name := range queries { + if pkg.Name == name { + response.addRoot(pkg.ID) + break + } + } + } + } + + if len(simpleMatches) != 0 { + resp, err := driver(cfg, simpleMatches...) + if err != nil { + return err + } + addResponse(resp) + } + + // Module cache matches are tricky. We want to avoid downloading new + // versions of things, so we need to use the ones present in the cache. + // go list doesn't accept version specifiers, so we have to write out a + // temporary module, and do the list in that module. + if len(modCacheMatches) != 0 { + // Collect all the matches, deduplicating by major version + // and preferring the newest. + type modInfo struct { + mod string + major string + } + mods := make(map[modInfo]string) + var imports []string + for _, modPath := range modCacheMatches { + matches := modCacheRegexp.FindStringSubmatch(modPath) + mod, ver := filepath.ToSlash(matches[1]), matches[2] + importPath := filepath.ToSlash(filepath.Join(matches[1], matches[3])) + + major := semver.Major(ver) + if prevVer, ok := mods[modInfo{mod, major}]; !ok || semver.Compare(ver, prevVer) > 0 { + mods[modInfo{mod, major}] = ver + } + + imports = append(imports, importPath) + } + + // Build the temporary module. + var gomod bytes.Buffer + gomod.WriteString("module modquery\nrequire (\n") + for mod, version := range mods { + gomod.WriteString("\t" + mod.mod + " " + version + "\n") + } + gomod.WriteString(")\n") + + tmpCfg := *cfg + + // We're only trying to look at stuff in the module cache, so + // disable the network. This should speed things up, and has + // prevented errors in at least one case, #28518. + tmpCfg.Env = append(append([]string{"GOPROXY=off"}, cfg.Env...)) + + var err error + tmpCfg.Dir, err = ioutil.TempDir("", "gopackages-modquery") + if err != nil { + return err + } + defer os.RemoveAll(tmpCfg.Dir) + + if err := ioutil.WriteFile(filepath.Join(tmpCfg.Dir, "go.mod"), gomod.Bytes(), 0777); err != nil { + return fmt.Errorf("writing go.mod for module cache query: %v", err) + } + + // Run the query, using the import paths calculated from the matches above. + resp, err := driver(&tmpCfg, imports...) + if err != nil { + return fmt.Errorf("querying module cache matches: %v", err) + } + addResponse(resp) + } + + return nil +} + +func getSizes(cfg *Config) (types.Sizes, error) { + return packagesdriver.GetSizesGolist(cfg.Context, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg)) +} + +// roots selects the appropriate paths to walk based on the passed-in configuration, +// particularly the environment and the presence of a go.mod in cfg.Dir's parents. +func roots(cfg *Config) ([]gopathwalk.Root, string, error) { + stdout, err := invokeGo(cfg, "env", "GOROOT", "GOPATH", "GOMOD") + if err != nil { + return nil, "", err + } + + fields := strings.Split(stdout.String(), "\n") + if len(fields) != 4 || len(fields[3]) != 0 { + return nil, "", fmt.Errorf("go env returned unexpected output: %q", stdout.String()) + } + goroot, gopath, gomod := fields[0], filepath.SplitList(fields[1]), fields[2] + var modDir string + if gomod != "" { + modDir = filepath.Dir(gomod) + } + + var roots []gopathwalk.Root + // Always add GOROOT. + roots = append(roots, gopathwalk.Root{filepath.Join(goroot, "/src"), gopathwalk.RootGOROOT}) + // If modules are enabled, scan the module dir. + if modDir != "" { + roots = append(roots, gopathwalk.Root{modDir, gopathwalk.RootCurrentModule}) + } + // Add either GOPATH/src or GOPATH/pkg/mod, depending on module mode. + for _, p := range gopath { + if modDir != "" { + roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache}) + } else { + roots = append(roots, gopathwalk.Root{filepath.Join(p, "/src"), gopathwalk.RootGOPATH}) + } + } + + return roots, modDir, nil +} + +// These functions were copied from goimports. See further documentation there. + +// pathMatchesQueries is adapted from pkgIsCandidate. +// TODO: is it reasonable to do Contains here, rather than an exact match on a path component? +func pathMatchesQueries(path string, queries []string) bool { + lastTwo := lastTwoComponents(path) + for _, query := range queries { + if strings.Contains(lastTwo, query) { + return true + } + if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(query) { + lastTwo = lowerASCIIAndRemoveHyphen(lastTwo) + if strings.Contains(lastTwo, query) { + return true + } + } + } + return false +} + +// lastTwoComponents returns at most the last two path components +// of v, using either / or \ as the path separator. +func lastTwoComponents(v string) string { + nslash := 0 + for i := len(v) - 1; i >= 0; i-- { + if v[i] == '/' || v[i] == '\\' { + nslash++ + if nslash == 2 { + return v[i:] + } + } + } + return v +} + +func hasHyphenOrUpperASCII(s string) bool { + for i := 0; i < len(s); i++ { + b := s[i] + if b == '-' || ('A' <= b && b <= 'Z') { + return true + } + } + return false +} + +func lowerASCIIAndRemoveHyphen(s string) (ret string) { + buf := make([]byte, 0, len(s)) + for i := 0; i < len(s); i++ { + b := s[i] + switch { + case b == '-': + continue + case 'A' <= b && b <= 'Z': + buf = append(buf, b+('a'-'A')) + default: + buf = append(buf, b) + } + } + return string(buf) +} + +// Fields must match go list; +// see $GOROOT/src/cmd/go/internal/load/pkg.go. +type jsonPackage struct { + ImportPath string + Dir string + Name string + Export string + GoFiles []string + CompiledGoFiles []string + CFiles []string + CgoFiles []string + CXXFiles []string + MFiles []string + HFiles []string + FFiles []string + SFiles []string + SwigFiles []string + SwigCXXFiles []string + SysoFiles []string + Imports []string + ImportMap map[string]string + Deps []string + TestGoFiles []string + TestImports []string + XTestGoFiles []string + XTestImports []string + ForTest string // q in a "p [q.test]" package, else "" + DepOnly bool + + Error *jsonPackageError +} + +type jsonPackageError struct { + ImportStack []string + Pos string + Err string +} + +func otherFiles(p *jsonPackage) [][]string { + return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles} +} + +// golistDriver uses the "go list" command to expand the pattern +// words and return metadata for the specified packages. dir may be +// "" and env may be nil, as per os/exec.Command. +func golistDriver(cfg *Config, words ...string) (*driverResponse, error) { + // go list uses the following identifiers in ImportPath and Imports: + // + // "p" -- importable package or main (command) + // "q.test" -- q's test executable + // "p [q.test]" -- variant of p as built for q's test executable + // "q_test [q.test]" -- q's external test package + // + // The packages p that are built differently for a test q.test + // are q itself, plus any helpers used by the external test q_test, + // typically including "testing" and all its dependencies. + + // Run "go list" for complete + // information on the specified packages. + buf, err := invokeGo(cfg, golistargs(cfg, words)...) + if err != nil { + return nil, err + } + seen := make(map[string]*jsonPackage) + // Decode the JSON and convert it to Package form. + var response driverResponse + for dec := json.NewDecoder(buf); dec.More(); { + p := new(jsonPackage) + if err := dec.Decode(p); err != nil { + return nil, fmt.Errorf("JSON decoding failed: %v", err) + } + + if p.ImportPath == "" { + // The documentation for go list says that “[e]rroneous packages will have + // a non-empty ImportPath”. If for some reason it comes back empty, we + // prefer to error out rather than silently discarding data or handing + // back a package without any way to refer to it. + if p.Error != nil { + return nil, Error{ + Pos: p.Error.Pos, + Msg: p.Error.Err, + } + } + return nil, fmt.Errorf("package missing import path: %+v", p) + } + + if old, found := seen[p.ImportPath]; found { + if !reflect.DeepEqual(p, old) { + return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath) + } + // skip the duplicate + continue + } + seen[p.ImportPath] = p + + pkg := &Package{ + Name: p.Name, + ID: p.ImportPath, + GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles), + CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles), + OtherFiles: absJoin(p.Dir, otherFiles(p)...), + } + + // Work around https://golang.org/issue/28749: + // cmd/go puts assembly, C, and C++ files in CompiledGoFiles. + // Filter out any elements of CompiledGoFiles that are also in OtherFiles. + // We have to keep this workaround in place until go1.12 is a distant memory. + if len(pkg.OtherFiles) > 0 { + other := make(map[string]bool, len(pkg.OtherFiles)) + for _, f := range pkg.OtherFiles { + other[f] = true + } + + out := pkg.CompiledGoFiles[:0] + for _, f := range pkg.CompiledGoFiles { + if other[f] { + continue + } + out = append(out, f) + } + pkg.CompiledGoFiles = out + } + + // Extract the PkgPath from the package's ID. + if i := strings.IndexByte(pkg.ID, ' '); i >= 0 { + pkg.PkgPath = pkg.ID[:i] + } else { + pkg.PkgPath = pkg.ID + } + + if pkg.PkgPath == "unsafe" { + pkg.GoFiles = nil // ignore fake unsafe.go file + } + + // Assume go list emits only absolute paths for Dir. + if p.Dir != "" && !filepath.IsAbs(p.Dir) { + log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir) + } + + if p.Export != "" && !filepath.IsAbs(p.Export) { + pkg.ExportFile = filepath.Join(p.Dir, p.Export) + } else { + pkg.ExportFile = p.Export + } + + // imports + // + // Imports contains the IDs of all imported packages. + // ImportsMap records (path, ID) only where they differ. + ids := make(map[string]bool) + for _, id := range p.Imports { + ids[id] = true + } + pkg.Imports = make(map[string]*Package) + for path, id := range p.ImportMap { + pkg.Imports[path] = &Package{ID: id} // non-identity import + delete(ids, id) + } + for id := range ids { + if id == "C" { + continue + } + + pkg.Imports[id] = &Package{ID: id} // identity import + } + if !p.DepOnly { + response.Roots = append(response.Roots, pkg.ID) + } + + // Work around for pre-go.1.11 versions of go list. + // TODO(matloob): they should be handled by the fallback. + // Can we delete this? + if len(pkg.CompiledGoFiles) == 0 { + pkg.CompiledGoFiles = pkg.GoFiles + } + + if p.Error != nil { + pkg.Errors = append(pkg.Errors, Error{ + Pos: p.Error.Pos, + Msg: p.Error.Err, + }) + } + + response.Packages = append(response.Packages, pkg) + } + + return &response, nil +} + +// absJoin absolutizes and flattens the lists of files. +func absJoin(dir string, fileses ...[]string) (res []string) { + for _, files := range fileses { + for _, file := range files { + if !filepath.IsAbs(file) { + file = filepath.Join(dir, file) + } + res = append(res, file) + } + } + return res +} + +func golistargs(cfg *Config, words []string) []string { + const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo + fullargs := []string{ + "list", "-e", "-json", + fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypesInfo|NeedTypesSizes) != 0), + fmt.Sprintf("-test=%t", cfg.Tests), + fmt.Sprintf("-export=%t", usesExportData(cfg)), + fmt.Sprintf("-deps=%t", cfg.Mode&NeedDeps != 0), + // go list doesn't let you pass -test and -find together, + // probably because you'd just get the TestMain. + fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0), + } + fullargs = append(fullargs, cfg.BuildFlags...) + fullargs = append(fullargs, "--") + fullargs = append(fullargs, words...) + return fullargs +} + +// invokeGo returns the stdout of a go command invocation. +func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) { + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + cmd := exec.CommandContext(cfg.Context, "go", args...) + // On darwin the cwd gets resolved to the real path, which breaks anything that + // expects the working directory to keep the original path, including the + // go command when dealing with modules. + // The Go stdlib has a special feature where if the cwd and the PWD are the + // same node then it trusts the PWD, so by setting it in the env for the child + // process we fix up all the paths returned by the go command. + cmd.Env = append(append([]string{}, cfg.Env...), "PWD="+cfg.Dir) + cmd.Dir = cfg.Dir + cmd.Stdout = stdout + cmd.Stderr = stderr + if debug { + defer func(start time.Time) { + log.Printf("%s for %v, stderr: <<%s>>\n", time.Since(start), cmdDebugStr(cmd, args...), stderr) + }(time.Now()) + } + + if err := cmd.Run(); err != nil { + // Check for 'go' executable not being found. + if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound { + return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound) + } + + exitErr, ok := err.(*exec.ExitError) + if !ok { + // Catastrophic error: + // - context cancellation + return nil, fmt.Errorf("couldn't exec 'go %v': %s %T", args, err, err) + } + + // Old go version? + if strings.Contains(stderr.String(), "flag provided but not defined") { + return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)} + } + + // This error only appears in stderr. See golang.org/cl/166398 for a fix in go list to show + // the error in the Err section of stdout in case -e option is provided. + // This fix is provided for backwards compatibility. + if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must be .go files") { + output := fmt.Sprintf(`{"ImportPath": "","Incomplete": true,"Error": {"Pos": "","Err": %s}}`, + strconv.Quote(strings.Trim(stderr.String(), "\n"))) + return bytes.NewBufferString(output), nil + } + + // Export mode entails a build. + // If that build fails, errors appear on stderr + // (despite the -e flag) and the Export field is blank. + // Do not fail in that case. + // The same is true if an ad-hoc package given to go list doesn't exist. + // TODO(matloob): Remove these once we can depend on go list to exit with a zero status with -e even when + // packages don't exist or a build fails. + if !usesExportData(cfg) && !containsGoFile(args) { + return nil, fmt.Errorf("go %v: %s: %s", args, exitErr, stderr) + } + } + + // As of writing, go list -export prints some non-fatal compilation + // errors to stderr, even with -e set. We would prefer that it put + // them in the Package.Error JSON (see https://golang.org/issue/26319). + // In the meantime, there's nowhere good to put them, but they can + // be useful for debugging. Print them if $GOPACKAGESPRINTGOLISTERRORS + // is set. + if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTGOLISTERRORS") != "" { + fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd, args...), stderr) + } + + // debugging + if false { + fmt.Fprintf(os.Stderr, "%s stdout: <<%s>>\n", cmdDebugStr(cmd, args...), stdout) + } + + return stdout, nil +} + +func containsGoFile(s []string) bool { + for _, f := range s { + if strings.HasSuffix(f, ".go") { + return true + } + } + return false +} + +func cmdDebugStr(cmd *exec.Cmd, args ...string) string { + env := make(map[string]string) + for _, kv := range cmd.Env { + split := strings.Split(kv, "=") + k, v := split[0], split[1] + env[k] = v + } + var quotedArgs []string + for _, arg := range args { + quotedArgs = append(quotedArgs, strconv.Quote(arg)) + } + + return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v PWD=%v go %s", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["PWD"], strings.Join(quotedArgs, " ")) +} diff --git a/vendor/golang.org/x/tools/go/packages/golist_overlay.go b/vendor/golang.org/x/tools/go/packages/golist_overlay.go new file mode 100644 index 0000000000..71ffcd9d55 --- /dev/null +++ b/vendor/golang.org/x/tools/go/packages/golist_overlay.go @@ -0,0 +1,104 @@ +package packages + +import ( + "go/parser" + "go/token" + "path/filepath" + "strconv" + "strings" +) + +// processGolistOverlay provides rudimentary support for adding +// files that don't exist on disk to an overlay. The results can be +// sometimes incorrect. +// TODO(matloob): Handle unsupported cases, including the following: +// - test files +// - adding test and non-test files to test variants of packages +// - determining the correct package to add given a new import path +// - creating packages that don't exist +func processGolistOverlay(cfg *Config, response *driverResponse) (modifiedPkgs, needPkgs []string, err error) { + havePkgs := make(map[string]string) // importPath -> non-test package ID + needPkgsSet := make(map[string]bool) + modifiedPkgsSet := make(map[string]bool) + + for _, pkg := range response.Packages { + // This is an approximation of import path to id. This can be + // wrong for tests, vendored packages, and a number of other cases. + havePkgs[pkg.PkgPath] = pkg.ID + } + +outer: + for path, contents := range cfg.Overlay { + base := filepath.Base(path) + if strings.HasSuffix(path, "_test.go") { + // Overlays don't support adding new test files yet. + // TODO(matloob): support adding new test files. + continue + } + dir := filepath.Dir(path) + for _, pkg := range response.Packages { + var dirContains, fileExists bool + for _, f := range pkg.GoFiles { + if sameFile(filepath.Dir(f), dir) { + dirContains = true + } + if filepath.Base(f) == base { + fileExists = true + } + } + if dirContains { + if !fileExists { + pkg.GoFiles = append(pkg.GoFiles, path) // TODO(matloob): should the file just be added to GoFiles? + pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, path) + modifiedPkgsSet[pkg.ID] = true + } + imports, err := extractImports(path, contents) + if err != nil { + // Let the parser or type checker report errors later. + continue outer + } + for _, imp := range imports { + _, found := pkg.Imports[imp] + if !found { + needPkgsSet[imp] = true + // TODO(matloob): Handle cases when the following block isn't correct. + // These include imports of test variants, imports of vendored packages, etc. + id, ok := havePkgs[imp] + if !ok { + id = imp + } + pkg.Imports[imp] = &Package{ID: id} + } + } + continue outer + } + } + } + + needPkgs = make([]string, 0, len(needPkgsSet)) + for pkg := range needPkgsSet { + needPkgs = append(needPkgs, pkg) + } + modifiedPkgs = make([]string, 0, len(modifiedPkgsSet)) + for pkg := range modifiedPkgsSet { + modifiedPkgs = append(modifiedPkgs, pkg) + } + return modifiedPkgs, needPkgs, err +} + +func extractImports(filename string, contents []byte) ([]string, error) { + f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset? + if err != nil { + return nil, err + } + var res []string + for _, imp := range f.Imports { + quotedPath := imp.Path.Value + path, err := strconv.Unquote(quotedPath) + if err != nil { + return nil, err + } + res = append(res, path) + } + return res, nil +} diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go new file mode 100644 index 0000000000..4639fcddd7 --- /dev/null +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -0,0 +1,1059 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packages + +// See doc.go for package documentation and implementation notes. + +import ( + "context" + "encoding/json" + "fmt" + "go/ast" + "go/parser" + "go/scanner" + "go/token" + "go/types" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + "sync" + + "golang.org/x/tools/go/gcexportdata" +) + +// A LoadMode specifies the amount of detail to return when loading. +// Higher-numbered modes cause Load to return more information, +// but may be slower. Load may return more information than requested. +type LoadMode int + +const ( + // The following constants are used to specify which fields of the Package + // should be filled when loading is done. As a special case to provide + // backwards compatibility, a LoadMode of 0 is equivalent to LoadFiles. + // For all other LoadModes, the bits below specify which fields will be filled + // in the result packages. + // WARNING: This part of the go/packages API is EXPERIMENTAL. It might + // be changed or removed up until April 15 2019. After that date it will + // be frozen. + // TODO(matloob): Remove this comment on April 15. + + // ID and Errors (if present) will always be filled. + + // NeedName adds Name and PkgPath. + NeedName LoadMode = 1 << iota + + // NeedFiles adds GoFiles and OtherFiles. + NeedFiles + + // NeedCompiledGoFiles adds CompiledGoFiles. + NeedCompiledGoFiles + + // NeedImports adds Imports. If NeedDeps is not set, the Imports field will contain + // "placeholder" Packages with only the ID set. + NeedImports + + // NeedDeps adds the fields requested by the LoadMode in the packages in Imports. If NeedImports + // is not set NeedDeps has no effect. + NeedDeps + + // NeedExportsFile adds ExportsFile. + NeedExportsFile + + // NeedTypes adds Types, Fset, and IllTyped. + NeedTypes + + // NeedSyntax adds Syntax. + NeedSyntax + + // NeedTypesInfo adds TypesInfo. + NeedTypesInfo + + // NeedTypesSizes adds TypesSizes. + NeedTypesSizes +) + +const ( + // LoadFiles finds the packages and computes their source file lists. + // Package fields: ID, Name, Errors, GoFiles, CompiledGoFiles, and OtherFiles. + LoadFiles = NeedName | NeedFiles | NeedCompiledGoFiles + + // LoadImports adds import information for each package + // and its dependencies. + // Package fields added: Imports. + LoadImports = LoadFiles | NeedImports | NeedDeps + + // LoadTypes adds type information for package-level + // declarations in the packages matching the patterns. + // Package fields added: Types, TypesSizes, Fset, and IllTyped. + // This mode uses type information provided by the build system when + // possible, and may fill in the ExportFile field. + LoadTypes = LoadImports | NeedTypes | NeedTypesSizes + + // LoadSyntax adds typed syntax trees for the packages matching the patterns. + // Package fields added: Syntax, and TypesInfo, for direct pattern matches only. + LoadSyntax = LoadTypes | NeedSyntax | NeedTypesInfo + + // LoadAllSyntax adds typed syntax trees for the packages matching the patterns + // and all dependencies. + // Package fields added: Types, Fset, IllTyped, Syntax, and TypesInfo, + // for all packages in the import graph. + LoadAllSyntax = LoadSyntax +) + +// A Config specifies details about how packages should be loaded. +// The zero value is a valid configuration. +// Calls to Load do not modify this struct. +type Config struct { + // Mode controls the level of information returned for each package. + Mode LoadMode + + // Context specifies the context for the load operation. + // If the context is cancelled, the loader may stop early + // and return an ErrCancelled error. + // If Context is nil, the load cannot be cancelled. + Context context.Context + + // Dir is the directory in which to run the build system's query tool + // that provides information about the packages. + // If Dir is empty, the tool is run in the current directory. + Dir string + + // Env is the environment to use when invoking the build system's query tool. + // If Env is nil, the current environment is used. + // As in os/exec's Cmd, only the last value in the slice for + // each environment key is used. To specify the setting of only + // a few variables, append to the current environment, as in: + // + // opt.Env = append(os.Environ(), "GOOS=plan9", "GOARCH=386") + // + Env []string + + // BuildFlags is a list of command-line flags to be passed through to + // the build system's query tool. + BuildFlags []string + + // Fset provides source position information for syntax trees and types. + // If Fset is nil, Load will use a new fileset, but preserve Fset's value. + Fset *token.FileSet + + // ParseFile is called to read and parse each file + // when preparing a package's type-checked syntax tree. + // It must be safe to call ParseFile simultaneously from multiple goroutines. + // If ParseFile is nil, the loader will uses parser.ParseFile. + // + // ParseFile should parse the source from src and use filename only for + // recording position information. + // + // An application may supply a custom implementation of ParseFile + // to change the effective file contents or the behavior of the parser, + // or to modify the syntax tree. For example, selectively eliminating + // unwanted function bodies can significantly accelerate type checking. + ParseFile func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) + + // If Tests is set, the loader includes not just the packages + // matching a particular pattern but also any related test packages, + // including test-only variants of the package and the test executable. + // + // For example, when using the go command, loading "fmt" with Tests=true + // returns four packages, with IDs "fmt" (the standard package), + // "fmt [fmt.test]" (the package as compiled for the test), + // "fmt_test" (the test functions from source files in package fmt_test), + // and "fmt.test" (the test binary). + // + // In build systems with explicit names for tests, + // setting Tests may have no effect. + Tests bool + + // Overlay provides a mapping of absolute file paths to file contents. + // If the file with the given path already exists, the parser will use the + // alternative file contents provided by the map. + // + // Overlays provide incomplete support for when a given file doesn't + // already exist on disk. See the package doc above for more details. + Overlay map[string][]byte +} + +// driver is the type for functions that query the build system for the +// packages named by the patterns. +type driver func(cfg *Config, patterns ...string) (*driverResponse, error) + +// driverResponse contains the results for a driver query. +type driverResponse struct { + // Sizes, if not nil, is the types.Sizes to use when type checking. + Sizes *types.StdSizes + + // Roots is the set of package IDs that make up the root packages. + // We have to encode this separately because when we encode a single package + // we cannot know if it is one of the roots as that requires knowledge of the + // graph it is part of. + Roots []string `json:",omitempty"` + + // Packages is the full set of packages in the graph. + // The packages are not connected into a graph. + // The Imports if populated will be stubs that only have their ID set. + // Imports will be connected and then type and syntax information added in a + // later pass (see refine). + Packages []*Package +} + +// Load loads and returns the Go packages named by the given patterns. +// +// Config specifies loading options; +// nil behaves the same as an empty Config. +// +// Load returns an error if any of the patterns was invalid +// as defined by the underlying build system. +// It may return an empty list of packages without an error, +// for instance for an empty expansion of a valid wildcard. +// Errors associated with a particular package are recorded in the +// corresponding Package's Errors list, and do not cause Load to +// return an error. Clients may need to handle such errors before +// proceeding with further analysis. The PrintErrors function is +// provided for convenient display of all errors. +func Load(cfg *Config, patterns ...string) ([]*Package, error) { + l := newLoader(cfg) + response, err := defaultDriver(&l.Config, patterns...) + if err != nil { + return nil, err + } + l.sizes = response.Sizes + return l.refine(response.Roots, response.Packages...) +} + +// defaultDriver is a driver that looks for an external driver binary, and if +// it does not find it falls back to the built in go list driver. +func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) { + driver := findExternalDriver(cfg) + if driver == nil { + driver = goListDriver + } + return driver(cfg, patterns...) +} + +// A Package describes a loaded Go package. +type Package struct { + // ID is a unique identifier for a package, + // in a syntax provided by the underlying build system. + // + // Because the syntax varies based on the build system, + // clients should treat IDs as opaque and not attempt to + // interpret them. + ID string + + // Name is the package name as it appears in the package source code. + Name string + + // PkgPath is the package path as used by the go/types package. + PkgPath string + + // Errors contains any errors encountered querying the metadata + // of the package, or while parsing or type-checking its files. + Errors []Error + + // GoFiles lists the absolute file paths of the package's Go source files. + GoFiles []string + + // CompiledGoFiles lists the absolute file paths of the package's source + // files that were presented to the compiler. + // This may differ from GoFiles if files are processed before compilation. + CompiledGoFiles []string + + // OtherFiles lists the absolute file paths of the package's non-Go source files, + // including assembly, C, C++, Fortran, Objective-C, SWIG, and so on. + OtherFiles []string + + // ExportFile is the absolute path to a file containing type + // information for the package as provided by the build system. + ExportFile string + + // Imports maps import paths appearing in the package's Go source files + // to corresponding loaded Packages. + Imports map[string]*Package + + // Types provides type information for the package. + // Modes LoadTypes and above set this field for packages matching the + // patterns; type information for dependencies may be missing or incomplete. + // Mode LoadAllSyntax sets this field for all packages, including dependencies. + Types *types.Package + + // Fset provides position information for Types, TypesInfo, and Syntax. + // It is set only when Types is set. + Fset *token.FileSet + + // IllTyped indicates whether the package or any dependency contains errors. + // It is set only when Types is set. + IllTyped bool + + // Syntax is the package's syntax trees, for the files listed in CompiledGoFiles. + // + // Mode LoadSyntax sets this field for packages matching the patterns. + // Mode LoadAllSyntax sets this field for all packages, including dependencies. + Syntax []*ast.File + + // TypesInfo provides type information about the package's syntax trees. + // It is set only when Syntax is set. + TypesInfo *types.Info + + // TypesSizes provides the effective size function for types in TypesInfo. + TypesSizes types.Sizes +} + +// An Error describes a problem with a package's metadata, syntax, or types. +type Error struct { + Pos string // "file:line:col" or "file:line" or "" or "-" + Msg string + Kind ErrorKind +} + +// ErrorKind describes the source of the error, allowing the user to +// differentiate between errors generated by the driver, the parser, or the +// type-checker. +type ErrorKind int + +const ( + UnknownError ErrorKind = iota + ListError + ParseError + TypeError +) + +func (err Error) Error() string { + pos := err.Pos + if pos == "" { + pos = "-" // like token.Position{}.String() + } + return pos + ": " + err.Msg +} + +// flatPackage is the JSON form of Package +// It drops all the type and syntax fields, and transforms the Imports +// +// TODO(adonovan): identify this struct with Package, effectively +// publishing the JSON protocol. +type flatPackage struct { + ID string + Name string `json:",omitempty"` + PkgPath string `json:",omitempty"` + Errors []Error `json:",omitempty"` + GoFiles []string `json:",omitempty"` + CompiledGoFiles []string `json:",omitempty"` + OtherFiles []string `json:",omitempty"` + ExportFile string `json:",omitempty"` + Imports map[string]string `json:",omitempty"` +} + +// MarshalJSON returns the Package in its JSON form. +// For the most part, the structure fields are written out unmodified, and +// the type and syntax fields are skipped. +// The imports are written out as just a map of path to package id. +// The errors are written using a custom type that tries to preserve the +// structure of error types we know about. +// +// This method exists to enable support for additional build systems. It is +// not intended for use by clients of the API and we may change the format. +func (p *Package) MarshalJSON() ([]byte, error) { + flat := &flatPackage{ + ID: p.ID, + Name: p.Name, + PkgPath: p.PkgPath, + Errors: p.Errors, + GoFiles: p.GoFiles, + CompiledGoFiles: p.CompiledGoFiles, + OtherFiles: p.OtherFiles, + ExportFile: p.ExportFile, + } + if len(p.Imports) > 0 { + flat.Imports = make(map[string]string, len(p.Imports)) + for path, ipkg := range p.Imports { + flat.Imports[path] = ipkg.ID + } + } + return json.Marshal(flat) +} + +// UnmarshalJSON reads in a Package from its JSON format. +// See MarshalJSON for details about the format accepted. +func (p *Package) UnmarshalJSON(b []byte) error { + flat := &flatPackage{} + if err := json.Unmarshal(b, &flat); err != nil { + return err + } + *p = Package{ + ID: flat.ID, + Name: flat.Name, + PkgPath: flat.PkgPath, + Errors: flat.Errors, + GoFiles: flat.GoFiles, + CompiledGoFiles: flat.CompiledGoFiles, + OtherFiles: flat.OtherFiles, + ExportFile: flat.ExportFile, + } + if len(flat.Imports) > 0 { + p.Imports = make(map[string]*Package, len(flat.Imports)) + for path, id := range flat.Imports { + p.Imports[path] = &Package{ID: id} + } + } + return nil +} + +func (p *Package) String() string { return p.ID } + +// loaderPackage augments Package with state used during the loading phase +type loaderPackage struct { + *Package + importErrors map[string]error // maps each bad import to its error + loadOnce sync.Once + color uint8 // for cycle detection + needsrc bool // load from source (Mode >= LoadTypes) + needtypes bool // type information is either requested or depended on + initial bool // package was matched by a pattern +} + +// loader holds the working state of a single call to load. +type loader struct { + pkgs map[string]*loaderPackage + Config + sizes types.Sizes + exportMu sync.Mutex // enforces mutual exclusion of exportdata operations + + // TODO(matloob): Add an implied mode here and use that instead of mode. + // Implied mode would contain all the fields we need the data for so we can + // get the actually requested fields. We'll zero them out before returning + // packages to the user. This will make it easier for us to get the conditions + // where we need certain modes right. +} + +func newLoader(cfg *Config) *loader { + ld := &loader{} + if cfg != nil { + ld.Config = *cfg + } + if ld.Config.Mode == 0 { + ld.Config.Mode = LoadFiles // Preserve zero behavior of Mode for backwards compatibility. + } + if ld.Config.Env == nil { + ld.Config.Env = os.Environ() + } + if ld.Context == nil { + ld.Context = context.Background() + } + if ld.Dir == "" { + if dir, err := os.Getwd(); err == nil { + ld.Dir = dir + } + } + + if ld.Mode&NeedTypes != 0 { + if ld.Fset == nil { + ld.Fset = token.NewFileSet() + } + + // ParseFile is required even in LoadTypes mode + // because we load source if export data is missing. + if ld.ParseFile == nil { + ld.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) { + var isrc interface{} + if src != nil { + isrc = src + } + const mode = parser.AllErrors | parser.ParseComments + return parser.ParseFile(fset, filename, isrc, mode) + } + } + } + return ld +} + +// refine connects the supplied packages into a graph and then adds type and +// and syntax information as requested by the LoadMode. +func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) { + rootMap := make(map[string]int, len(roots)) + for i, root := range roots { + rootMap[root] = i + } + ld.pkgs = make(map[string]*loaderPackage) + // first pass, fixup and build the map and roots + var initial = make([]*loaderPackage, len(roots)) + for _, pkg := range list { + rootIndex := -1 + if i, found := rootMap[pkg.ID]; found { + rootIndex = i + } + lpkg := &loaderPackage{ + Package: pkg, + needtypes: (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && rootIndex < 0) || rootIndex >= 0, + needsrc: (ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && rootIndex < 0) || rootIndex >= 0 || + len(ld.Overlay) > 0 || // Overlays can invalidate export data. TODO(matloob): make this check fine-grained based on dependencies on overlaid files + pkg.ExportFile == "" && pkg.PkgPath != "unsafe", + } + ld.pkgs[lpkg.ID] = lpkg + if rootIndex >= 0 { + initial[rootIndex] = lpkg + lpkg.initial = true + } + } + for i, root := range roots { + if initial[i] == nil { + return nil, fmt.Errorf("root package %v is missing", root) + } + } + + // Materialize the import graph. + + const ( + white = 0 // new + grey = 1 // in progress + black = 2 // complete + ) + + // visit traverses the import graph, depth-first, + // and materializes the graph as Packages.Imports. + // + // Valid imports are saved in the Packages.Import map. + // Invalid imports (cycles and missing nodes) are saved in the importErrors map. + // Thus, even in the presence of both kinds of errors, the Import graph remains a DAG. + // + // visit returns whether the package needs src or has a transitive + // dependency on a package that does. These are the only packages + // for which we load source code. + var stack []*loaderPackage + var visit func(lpkg *loaderPackage) bool + var srcPkgs []*loaderPackage + visit = func(lpkg *loaderPackage) bool { + switch lpkg.color { + case black: + return lpkg.needsrc + case grey: + panic("internal error: grey node") + } + lpkg.color = grey + stack = append(stack, lpkg) // push + stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports + lpkg.Imports = make(map[string]*Package, len(stubs)) + for importPath, ipkg := range stubs { + var importErr error + imp := ld.pkgs[ipkg.ID] + if imp == nil { + // (includes package "C" when DisableCgo) + importErr = fmt.Errorf("missing package: %q", ipkg.ID) + } else if imp.color == grey { + importErr = fmt.Errorf("import cycle: %s", stack) + } + if importErr != nil { + if lpkg.importErrors == nil { + lpkg.importErrors = make(map[string]error) + } + lpkg.importErrors[importPath] = importErr + continue + } + + if visit(imp) { + lpkg.needsrc = true + } + lpkg.Imports[importPath] = imp.Package + } + if lpkg.needsrc { + srcPkgs = append(srcPkgs, lpkg) + } + if ld.Mode&NeedTypesSizes != 0 { + lpkg.TypesSizes = ld.sizes + } + stack = stack[:len(stack)-1] // pop + lpkg.color = black + + return lpkg.needsrc + } + + if ld.Mode&(NeedImports|NeedDeps) == 0 { + // We do this to drop the stub import packages that we are not even going to try to resolve. + for _, lpkg := range initial { + lpkg.Imports = nil + } + } else { + // For each initial package, create its import DAG. + for _, lpkg := range initial { + visit(lpkg) + } + } + if ld.Mode&NeedDeps != 0 { // TODO(matloob): This is only the case if NeedTypes is also set, right? + for _, lpkg := range srcPkgs { + // Complete type information is required for the + // immediate dependencies of each source package. + for _, ipkg := range lpkg.Imports { + imp := ld.pkgs[ipkg.ID] + imp.needtypes = true + } + } + } + // Load type data if needed, starting at + // the initial packages (roots of the import DAG). + if ld.Mode&NeedTypes != 0 { + var wg sync.WaitGroup + for _, lpkg := range initial { + wg.Add(1) + go func(lpkg *loaderPackage) { + ld.loadRecursive(lpkg) + wg.Done() + }(lpkg) + } + wg.Wait() + } + + result := make([]*Package, len(initial)) + importPlaceholders := make(map[string]*Package) + for i, lpkg := range initial { + result[i] = lpkg.Package + } + for i := range ld.pkgs { + // Clear all unrequested fields, for extra de-Hyrum-ization. + if ld.Mode&NeedName == 0 { + ld.pkgs[i].Name = "" + ld.pkgs[i].PkgPath = "" + } + if ld.Mode&NeedFiles == 0 { + ld.pkgs[i].GoFiles = nil + ld.pkgs[i].OtherFiles = nil + } + if ld.Mode&NeedCompiledGoFiles == 0 { + ld.pkgs[i].CompiledGoFiles = nil + } + if ld.Mode&NeedImports == 0 { + ld.pkgs[i].Imports = nil + } + if ld.Mode&NeedExportsFile == 0 { + ld.pkgs[i].ExportFile = "" + } + if ld.Mode&NeedTypes == 0 { + ld.pkgs[i].Types = nil + ld.pkgs[i].Fset = nil + ld.pkgs[i].IllTyped = false + } + if ld.Mode&NeedSyntax == 0 { + ld.pkgs[i].Syntax = nil + } + if ld.Mode&NeedTypesInfo == 0 { + ld.pkgs[i].TypesInfo = nil + } + if ld.Mode&NeedTypesSizes == 0 { + ld.pkgs[i].TypesSizes = nil + } + if ld.Mode&NeedDeps == 0 { + for j, pkg := range ld.pkgs[i].Imports { + ph, ok := importPlaceholders[pkg.ID] + if !ok { + ph = &Package{ID: pkg.ID} + importPlaceholders[pkg.ID] = ph + } + ld.pkgs[i].Imports[j] = ph + } + } + } + return result, nil +} + +// loadRecursive loads the specified package and its dependencies, +// recursively, in parallel, in topological order. +// It is atomic and idempotent. +// Precondition: ld.Mode&NeedTypes. +func (ld *loader) loadRecursive(lpkg *loaderPackage) { + lpkg.loadOnce.Do(func() { + // Load the direct dependencies, in parallel. + var wg sync.WaitGroup + for _, ipkg := range lpkg.Imports { + imp := ld.pkgs[ipkg.ID] + wg.Add(1) + go func(imp *loaderPackage) { + ld.loadRecursive(imp) + wg.Done() + }(imp) + } + wg.Wait() + + ld.loadPackage(lpkg) + }) +} + +// loadPackage loads the specified package. +// It must be called only once per Package, +// after immediate dependencies are loaded. +// Precondition: ld.Mode >= LoadTypes. +func (ld *loader) loadPackage(lpkg *loaderPackage) { + if lpkg.PkgPath == "unsafe" { + // Fill in the blanks to avoid surprises. + lpkg.Types = types.Unsafe + lpkg.Fset = ld.Fset + lpkg.Syntax = []*ast.File{} + lpkg.TypesInfo = new(types.Info) + lpkg.TypesSizes = ld.sizes + return + } + + // Call NewPackage directly with explicit name. + // This avoids skew between golist and go/types when the files' + // package declarations are inconsistent. + lpkg.Types = types.NewPackage(lpkg.PkgPath, lpkg.Name) + lpkg.Fset = ld.Fset + + // Subtle: we populate all Types fields with an empty Package + // before loading export data so that export data processing + // never has to create a types.Package for an indirect dependency, + // which would then require that such created packages be explicitly + // inserted back into the Import graph as a final step after export data loading. + // The Diamond test exercises this case. + if !lpkg.needtypes { + return + } + if !lpkg.needsrc { + ld.loadFromExportData(lpkg) + return // not a source package, don't get syntax trees + } + + appendError := func(err error) { + // Convert various error types into the one true Error. + var errs []Error + switch err := err.(type) { + case Error: + // from driver + errs = append(errs, err) + + case *os.PathError: + // from parser + errs = append(errs, Error{ + Pos: err.Path + ":1", + Msg: err.Err.Error(), + Kind: ParseError, + }) + + case scanner.ErrorList: + // from parser + for _, err := range err { + errs = append(errs, Error{ + Pos: err.Pos.String(), + Msg: err.Msg, + Kind: ParseError, + }) + } + + case types.Error: + // from type checker + errs = append(errs, Error{ + Pos: err.Fset.Position(err.Pos).String(), + Msg: err.Msg, + Kind: TypeError, + }) + + default: + // unexpected impoverished error from parser? + errs = append(errs, Error{ + Pos: "-", + Msg: err.Error(), + Kind: UnknownError, + }) + + // If you see this error message, please file a bug. + log.Printf("internal error: error %q (%T) without position", err, err) + } + + lpkg.Errors = append(lpkg.Errors, errs...) + } + + files, errs := ld.parseFiles(lpkg.CompiledGoFiles) + for _, err := range errs { + appendError(err) + } + + lpkg.Syntax = files + + lpkg.TypesInfo = &types.Info{ + Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), + Uses: make(map[*ast.Ident]types.Object), + Implicits: make(map[ast.Node]types.Object), + Scopes: make(map[ast.Node]*types.Scope), + Selections: make(map[*ast.SelectorExpr]*types.Selection), + } + lpkg.TypesSizes = ld.sizes + + importer := importerFunc(func(path string) (*types.Package, error) { + if path == "unsafe" { + return types.Unsafe, nil + } + + // The imports map is keyed by import path. + ipkg := lpkg.Imports[path] + if ipkg == nil { + if err := lpkg.importErrors[path]; err != nil { + return nil, err + } + // There was skew between the metadata and the + // import declarations, likely due to an edit + // race, or because the ParseFile feature was + // used to supply alternative file contents. + return nil, fmt.Errorf("no metadata for %s", path) + } + + if ipkg.Types != nil && ipkg.Types.Complete() { + return ipkg.Types, nil + } + log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg) + panic("unreachable") + }) + + // type-check + tc := &types.Config{ + Importer: importer, + + // Type-check bodies of functions only in non-initial packages. + // Example: for import graph A->B->C and initial packages {A,C}, + // we can ignore function bodies in B. + IgnoreFuncBodies: (ld.Mode&(NeedDeps|NeedTypesInfo) == 0) && !lpkg.initial, + + Error: appendError, + Sizes: ld.sizes, + } + types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax) + + lpkg.importErrors = nil // no longer needed + + // If !Cgo, the type-checker uses FakeImportC mode, so + // it doesn't invoke the importer for import "C", + // nor report an error for the import, + // or for any undefined C.f reference. + // We must detect this explicitly and correctly + // mark the package as IllTyped (by reporting an error). + // TODO(adonovan): if these errors are annoying, + // we could just set IllTyped quietly. + if tc.FakeImportC { + outer: + for _, f := range lpkg.Syntax { + for _, imp := range f.Imports { + if imp.Path.Value == `"C"` { + err := types.Error{Fset: ld.Fset, Pos: imp.Pos(), Msg: `import "C" ignored`} + appendError(err) + break outer + } + } + } + } + + // Record accumulated errors. + illTyped := len(lpkg.Errors) > 0 + if !illTyped { + for _, imp := range lpkg.Imports { + if imp.IllTyped { + illTyped = true + break + } + } + } + lpkg.IllTyped = illTyped +} + +// An importFunc is an implementation of the single-method +// types.Importer interface based on a function value. +type importerFunc func(path string) (*types.Package, error) + +func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) } + +// We use a counting semaphore to limit +// the number of parallel I/O calls per process. +var ioLimit = make(chan bool, 20) + +// parseFiles reads and parses the Go source files and returns the ASTs +// of the ones that could be at least partially parsed, along with a +// list of I/O and parse errors encountered. +// +// Because files are scanned in parallel, the token.Pos +// positions of the resulting ast.Files are not ordered. +// +func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) { + var wg sync.WaitGroup + n := len(filenames) + parsed := make([]*ast.File, n) + errors := make([]error, n) + for i, file := range filenames { + if ld.Config.Context.Err() != nil { + parsed[i] = nil + errors[i] = ld.Config.Context.Err() + continue + } + wg.Add(1) + go func(i int, filename string) { + ioLimit <- true // wait + // ParseFile may return both an AST and an error. + var src []byte + for f, contents := range ld.Config.Overlay { + if sameFile(f, filename) { + src = contents + } + } + var err error + if src == nil { + src, err = ioutil.ReadFile(filename) + } + if err != nil { + parsed[i], errors[i] = nil, err + } else { + parsed[i], errors[i] = ld.ParseFile(ld.Fset, filename, src) + } + <-ioLimit // signal + wg.Done() + }(i, file) + } + wg.Wait() + + // Eliminate nils, preserving order. + var o int + for _, f := range parsed { + if f != nil { + parsed[o] = f + o++ + } + } + parsed = parsed[:o] + + o = 0 + for _, err := range errors { + if err != nil { + errors[o] = err + o++ + } + } + errors = errors[:o] + + return parsed, errors +} + +// sameFile returns true if x and y have the same basename and denote +// the same file. +// +func sameFile(x, y string) bool { + if x == y { + // It could be the case that y doesn't exist. + // For instance, it may be an overlay file that + // hasn't been written to disk. To handle that case + // let x == y through. (We added the exact absolute path + // string to the CompiledGoFiles list, so the unwritten + // overlay case implies x==y.) + return true + } + if strings.EqualFold(filepath.Base(x), filepath.Base(y)) { // (optimisation) + if xi, err := os.Stat(x); err == nil { + if yi, err := os.Stat(y); err == nil { + return os.SameFile(xi, yi) + } + } + } + return false +} + +// loadFromExportData returns type information for the specified +// package, loading it from an export data file on the first request. +func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error) { + if lpkg.PkgPath == "" { + log.Fatalf("internal error: Package %s has no PkgPath", lpkg) + } + + // Because gcexportdata.Read has the potential to create or + // modify the types.Package for each node in the transitive + // closure of dependencies of lpkg, all exportdata operations + // must be sequential. (Finer-grained locking would require + // changes to the gcexportdata API.) + // + // The exportMu lock guards the Package.Pkg field and the + // types.Package it points to, for each Package in the graph. + // + // Not all accesses to Package.Pkg need to be protected by exportMu: + // graph ordering ensures that direct dependencies of source + // packages are fully loaded before the importer reads their Pkg field. + ld.exportMu.Lock() + defer ld.exportMu.Unlock() + + if tpkg := lpkg.Types; tpkg != nil && tpkg.Complete() { + return tpkg, nil // cache hit + } + + lpkg.IllTyped = true // fail safe + + if lpkg.ExportFile == "" { + // Errors while building export data will have been printed to stderr. + return nil, fmt.Errorf("no export data file") + } + f, err := os.Open(lpkg.ExportFile) + if err != nil { + return nil, err + } + defer f.Close() + + // Read gc export data. + // + // We don't currently support gccgo export data because all + // underlying workspaces use the gc toolchain. (Even build + // systems that support gccgo don't use it for workspace + // queries.) + r, err := gcexportdata.NewReader(f) + if err != nil { + return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err) + } + + // Build the view. + // + // The gcexportdata machinery has no concept of package ID. + // It identifies packages by their PkgPath, which although not + // globally unique is unique within the scope of one invocation + // of the linker, type-checker, or gcexportdata. + // + // So, we must build a PkgPath-keyed view of the global + // (conceptually ID-keyed) cache of packages and pass it to + // gcexportdata. The view must contain every existing + // package that might possibly be mentioned by the + // current package---its transitive closure. + // + // In loadPackage, we unconditionally create a types.Package for + // each dependency so that export data loading does not + // create new ones. + // + // TODO(adonovan): it would be simpler and more efficient + // if the export data machinery invoked a callback to + // get-or-create a package instead of a map. + // + view := make(map[string]*types.Package) // view seen by gcexportdata + seen := make(map[*loaderPackage]bool) // all visited packages + var visit func(pkgs map[string]*Package) + visit = func(pkgs map[string]*Package) { + for _, p := range pkgs { + lpkg := ld.pkgs[p.ID] + if !seen[lpkg] { + seen[lpkg] = true + view[lpkg.PkgPath] = lpkg.Types + visit(lpkg.Imports) + } + } + } + visit(lpkg.Imports) + + viewLen := len(view) + 1 // adding the self package + // Parse the export data. + // (May modify incomplete packages in view but not create new ones.) + tpkg, err := gcexportdata.Read(r, ld.Fset, view, lpkg.PkgPath) + if err != nil { + return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err) + } + if viewLen != len(view) { + log.Fatalf("Unexpected package creation during export data loading") + } + + lpkg.Types = tpkg + lpkg.IllTyped = false + + return tpkg, nil +} + +func usesExportData(cfg *Config) bool { + return cfg.Mode&NeedExportsFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedTypesInfo == 0 +} diff --git a/vendor/golang.org/x/tools/go/packages/visit.go b/vendor/golang.org/x/tools/go/packages/visit.go new file mode 100644 index 0000000000..b13cb081fc --- /dev/null +++ b/vendor/golang.org/x/tools/go/packages/visit.go @@ -0,0 +1,55 @@ +package packages + +import ( + "fmt" + "os" + "sort" +) + +// Visit visits all the packages in the import graph whose roots are +// pkgs, calling the optional pre function the first time each package +// is encountered (preorder), and the optional post function after a +// package's dependencies have been visited (postorder). +// The boolean result of pre(pkg) determines whether +// the imports of package pkg are visited. +func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) { + seen := make(map[*Package]bool) + var visit func(*Package) + visit = func(pkg *Package) { + if !seen[pkg] { + seen[pkg] = true + + if pre == nil || pre(pkg) { + paths := make([]string, 0, len(pkg.Imports)) + for path := range pkg.Imports { + paths = append(paths, path) + } + sort.Strings(paths) // Imports is a map, this makes visit stable + for _, path := range paths { + visit(pkg.Imports[path]) + } + } + + if post != nil { + post(pkg) + } + } + } + for _, pkg := range pkgs { + visit(pkg) + } +} + +// PrintErrors prints to os.Stderr the accumulated errors of all +// packages in the import graph rooted at pkgs, dependencies first. +// PrintErrors returns the number of errors printed. +func PrintErrors(pkgs []*Package) int { + var n int + Visit(pkgs, nil, func(pkg *Package) { + for _, err := range pkg.Errors { + fmt.Fprintln(os.Stderr, err) + n++ + } + }) + return n +} diff --git a/vendor/golang.org/x/tools/go/types/typeutil/callee.go b/vendor/golang.org/x/tools/go/types/typeutil/callee.go new file mode 100644 index 0000000000..38f596daf9 --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/typeutil/callee.go @@ -0,0 +1,46 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeutil + +import ( + "go/ast" + "go/types" + + "golang.org/x/tools/go/ast/astutil" +) + +// Callee returns the named target of a function call, if any: +// a function, method, builtin, or variable. +func Callee(info *types.Info, call *ast.CallExpr) types.Object { + var obj types.Object + switch fun := astutil.Unparen(call.Fun).(type) { + case *ast.Ident: + obj = info.Uses[fun] // type, var, builtin, or declared func + case *ast.SelectorExpr: + if sel, ok := info.Selections[fun]; ok { + obj = sel.Obj() // method or field + } else { + obj = info.Uses[fun.Sel] // qualified identifier? + } + } + if _, ok := obj.(*types.TypeName); ok { + return nil // T(x) is a conversion, not a call + } + return obj +} + +// StaticCallee returns the target (function or method) of a static +// function call, if any. It returns nil for calls to builtins. +func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func { + if f, ok := Callee(info, call).(*types.Func); ok && !interfaceMethod(f) { + return f + } + return nil +} + +func interfaceMethod(f *types.Func) bool { + recv := f.Type().(*types.Signature).Recv() + return recv != nil && types.IsInterface(recv.Type()) +} diff --git a/vendor/golang.org/x/tools/go/types/typeutil/imports.go b/vendor/golang.org/x/tools/go/types/typeutil/imports.go new file mode 100644 index 0000000000..9c441dba9c --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/typeutil/imports.go @@ -0,0 +1,31 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeutil + +import "go/types" + +// Dependencies returns all dependencies of the specified packages. +// +// Dependent packages appear in topological order: if package P imports +// package Q, Q appears earlier than P in the result. +// The algorithm follows import statements in the order they +// appear in the source code, so the result is a total order. +// +func Dependencies(pkgs ...*types.Package) []*types.Package { + var result []*types.Package + seen := make(map[*types.Package]bool) + var visit func(pkgs []*types.Package) + visit = func(pkgs []*types.Package) { + for _, p := range pkgs { + if !seen[p] { + seen[p] = true + visit(p.Imports()) + result = append(result, p) + } + } + } + visit(pkgs) + return result +} diff --git a/vendor/golang.org/x/tools/go/types/typeutil/map.go b/vendor/golang.org/x/tools/go/types/typeutil/map.go new file mode 100644 index 0000000000..c7f7545006 --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/typeutil/map.go @@ -0,0 +1,313 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package typeutil defines various utilities for types, such as Map, +// a mapping from types.Type to interface{} values. +package typeutil // import "golang.org/x/tools/go/types/typeutil" + +import ( + "bytes" + "fmt" + "go/types" + "reflect" +) + +// Map is a hash-table-based mapping from types (types.Type) to +// arbitrary interface{} values. The concrete types that implement +// the Type interface are pointers. Since they are not canonicalized, +// == cannot be used to check for equivalence, and thus we cannot +// simply use a Go map. +// +// Just as with map[K]V, a nil *Map is a valid empty map. +// +// Not thread-safe. +// +type Map struct { + hasher Hasher // shared by many Maps + table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused + length int // number of map entries +} + +// entry is an entry (key/value association) in a hash bucket. +type entry struct { + key types.Type + value interface{} +} + +// SetHasher sets the hasher used by Map. +// +// All Hashers are functionally equivalent but contain internal state +// used to cache the results of hashing previously seen types. +// +// A single Hasher created by MakeHasher() may be shared among many +// Maps. This is recommended if the instances have many keys in +// common, as it will amortize the cost of hash computation. +// +// A Hasher may grow without bound as new types are seen. Even when a +// type is deleted from the map, the Hasher never shrinks, since other +// types in the map may reference the deleted type indirectly. +// +// Hashers are not thread-safe, and read-only operations such as +// Map.Lookup require updates to the hasher, so a full Mutex lock (not a +// read-lock) is require around all Map operations if a shared +// hasher is accessed from multiple threads. +// +// If SetHasher is not called, the Map will create a private hasher at +// the first call to Insert. +// +func (m *Map) SetHasher(hasher Hasher) { + m.hasher = hasher +} + +// Delete removes the entry with the given key, if any. +// It returns true if the entry was found. +// +func (m *Map) Delete(key types.Type) bool { + if m != nil && m.table != nil { + hash := m.hasher.Hash(key) + bucket := m.table[hash] + for i, e := range bucket { + if e.key != nil && types.Identical(key, e.key) { + // We can't compact the bucket as it + // would disturb iterators. + bucket[i] = entry{} + m.length-- + return true + } + } + } + return false +} + +// At returns the map entry for the given key. +// The result is nil if the entry is not present. +// +func (m *Map) At(key types.Type) interface{} { + if m != nil && m.table != nil { + for _, e := range m.table[m.hasher.Hash(key)] { + if e.key != nil && types.Identical(key, e.key) { + return e.value + } + } + } + return nil +} + +// Set sets the map entry for key to val, +// and returns the previous entry, if any. +func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) { + if m.table != nil { + hash := m.hasher.Hash(key) + bucket := m.table[hash] + var hole *entry + for i, e := range bucket { + if e.key == nil { + hole = &bucket[i] + } else if types.Identical(key, e.key) { + prev = e.value + bucket[i].value = value + return + } + } + + if hole != nil { + *hole = entry{key, value} // overwrite deleted entry + } else { + m.table[hash] = append(bucket, entry{key, value}) + } + } else { + if m.hasher.memo == nil { + m.hasher = MakeHasher() + } + hash := m.hasher.Hash(key) + m.table = map[uint32][]entry{hash: {entry{key, value}}} + } + + m.length++ + return +} + +// Len returns the number of map entries. +func (m *Map) Len() int { + if m != nil { + return m.length + } + return 0 +} + +// Iterate calls function f on each entry in the map in unspecified order. +// +// If f should mutate the map, Iterate provides the same guarantees as +// Go maps: if f deletes a map entry that Iterate has not yet reached, +// f will not be invoked for it, but if f inserts a map entry that +// Iterate has not yet reached, whether or not f will be invoked for +// it is unspecified. +// +func (m *Map) Iterate(f func(key types.Type, value interface{})) { + if m != nil { + for _, bucket := range m.table { + for _, e := range bucket { + if e.key != nil { + f(e.key, e.value) + } + } + } + } +} + +// Keys returns a new slice containing the set of map keys. +// The order is unspecified. +func (m *Map) Keys() []types.Type { + keys := make([]types.Type, 0, m.Len()) + m.Iterate(func(key types.Type, _ interface{}) { + keys = append(keys, key) + }) + return keys +} + +func (m *Map) toString(values bool) string { + if m == nil { + return "{}" + } + var buf bytes.Buffer + fmt.Fprint(&buf, "{") + sep := "" + m.Iterate(func(key types.Type, value interface{}) { + fmt.Fprint(&buf, sep) + sep = ", " + fmt.Fprint(&buf, key) + if values { + fmt.Fprintf(&buf, ": %q", value) + } + }) + fmt.Fprint(&buf, "}") + return buf.String() +} + +// String returns a string representation of the map's entries. +// Values are printed using fmt.Sprintf("%v", v). +// Order is unspecified. +// +func (m *Map) String() string { + return m.toString(true) +} + +// KeysString returns a string representation of the map's key set. +// Order is unspecified. +// +func (m *Map) KeysString() string { + return m.toString(false) +} + +//////////////////////////////////////////////////////////////////////// +// Hasher + +// A Hasher maps each type to its hash value. +// For efficiency, a hasher uses memoization; thus its memory +// footprint grows monotonically over time. +// Hashers are not thread-safe. +// Hashers have reference semantics. +// Call MakeHasher to create a Hasher. +type Hasher struct { + memo map[types.Type]uint32 +} + +// MakeHasher returns a new Hasher instance. +func MakeHasher() Hasher { + return Hasher{make(map[types.Type]uint32)} +} + +// Hash computes a hash value for the given type t such that +// Identical(t, t') => Hash(t) == Hash(t'). +func (h Hasher) Hash(t types.Type) uint32 { + hash, ok := h.memo[t] + if !ok { + hash = h.hashFor(t) + h.memo[t] = hash + } + return hash +} + +// hashString computes the Fowler–Noll–Vo hash of s. +func hashString(s string) uint32 { + var h uint32 + for i := 0; i < len(s); i++ { + h ^= uint32(s[i]) + h *= 16777619 + } + return h +} + +// hashFor computes the hash of t. +func (h Hasher) hashFor(t types.Type) uint32 { + // See Identical for rationale. + switch t := t.(type) { + case *types.Basic: + return uint32(t.Kind()) + + case *types.Array: + return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem()) + + case *types.Slice: + return 9049 + 2*h.Hash(t.Elem()) + + case *types.Struct: + var hash uint32 = 9059 + for i, n := 0, t.NumFields(); i < n; i++ { + f := t.Field(i) + if f.Anonymous() { + hash += 8861 + } + hash += hashString(t.Tag(i)) + hash += hashString(f.Name()) // (ignore f.Pkg) + hash += h.Hash(f.Type()) + } + return hash + + case *types.Pointer: + return 9067 + 2*h.Hash(t.Elem()) + + case *types.Signature: + var hash uint32 = 9091 + if t.Variadic() { + hash *= 8863 + } + return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) + + case *types.Interface: + var hash uint32 = 9103 + for i, n := 0, t.NumMethods(); i < n; i++ { + // See go/types.identicalMethods for rationale. + // Method order is not significant. + // Ignore m.Pkg(). + m := t.Method(i) + hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type()) + } + return hash + + case *types.Map: + return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem()) + + case *types.Chan: + return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem()) + + case *types.Named: + // Not safe with a copying GC; objects may move. + return uint32(reflect.ValueOf(t.Obj()).Pointer()) + + case *types.Tuple: + return h.hashTuple(t) + } + panic(t) +} + +func (h Hasher) hashTuple(tuple *types.Tuple) uint32 { + // See go/types.identicalTypes for rationale. + n := tuple.Len() + var hash uint32 = 9137 + 2*uint32(n) + for i := 0; i < n; i++ { + hash += 3 * h.Hash(tuple.At(i).Type()) + } + return hash +} diff --git a/vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go b/vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go new file mode 100644 index 0000000000..32084610f4 --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go @@ -0,0 +1,72 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements a cache of method sets. + +package typeutil + +import ( + "go/types" + "sync" +) + +// A MethodSetCache records the method set of each type T for which +// MethodSet(T) is called so that repeat queries are fast. +// The zero value is a ready-to-use cache instance. +type MethodSetCache struct { + mu sync.Mutex + named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N + others map[types.Type]*types.MethodSet // all other types +} + +// MethodSet returns the method set of type T. It is thread-safe. +// +// If cache is nil, this function is equivalent to types.NewMethodSet(T). +// Utility functions can thus expose an optional *MethodSetCache +// parameter to clients that care about performance. +// +func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet { + if cache == nil { + return types.NewMethodSet(T) + } + cache.mu.Lock() + defer cache.mu.Unlock() + + switch T := T.(type) { + case *types.Named: + return cache.lookupNamed(T).value + + case *types.Pointer: + if N, ok := T.Elem().(*types.Named); ok { + return cache.lookupNamed(N).pointer + } + } + + // all other types + // (The map uses pointer equivalence, not type identity.) + mset := cache.others[T] + if mset == nil { + mset = types.NewMethodSet(T) + if cache.others == nil { + cache.others = make(map[types.Type]*types.MethodSet) + } + cache.others[T] = mset + } + return mset +} + +func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } { + if cache.named == nil { + cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet }) + } + // Avoid recomputing mset(*T) for each distinct Pointer + // instance whose underlying type is a named type. + msets, ok := cache.named[named] + if !ok { + msets.value = types.NewMethodSet(named) + msets.pointer = types.NewMethodSet(types.NewPointer(named)) + cache.named[named] = msets + } + return msets +} diff --git a/vendor/golang.org/x/tools/go/types/typeutil/ui.go b/vendor/golang.org/x/tools/go/types/typeutil/ui.go new file mode 100644 index 0000000000..9849c24cef --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/typeutil/ui.go @@ -0,0 +1,52 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeutil + +// This file defines utilities for user interfaces that display types. + +import "go/types" + +// IntuitiveMethodSet returns the intuitive method set of a type T, +// which is the set of methods you can call on an addressable value of +// that type. +// +// The result always contains MethodSet(T), and is exactly MethodSet(T) +// for interface types and for pointer-to-concrete types. +// For all other concrete types T, the result additionally +// contains each method belonging to *T if there is no identically +// named method on T itself. +// +// This corresponds to user intuition about method sets; +// this function is intended only for user interfaces. +// +// The order of the result is as for types.MethodSet(T). +// +func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection { + isPointerToConcrete := func(T types.Type) bool { + ptr, ok := T.(*types.Pointer) + return ok && !types.IsInterface(ptr.Elem()) + } + + var result []*types.Selection + mset := msets.MethodSet(T) + if types.IsInterface(T) || isPointerToConcrete(T) { + for i, n := 0, mset.Len(); i < n; i++ { + result = append(result, mset.At(i)) + } + } else { + // T is some other concrete type. + // Report methods of T and *T, preferring those of T. + pmset := msets.MethodSet(types.NewPointer(T)) + for i, n := 0, pmset.Len(); i < n; i++ { + meth := pmset.At(i) + if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil { + meth = m + } + result = append(result, meth) + } + + } + return result +} diff --git a/vendor/golang.org/x/tools/imports/fix.go b/vendor/golang.org/x/tools/imports/fix.go new file mode 100644 index 0000000000..777d28ccdb --- /dev/null +++ b/vendor/golang.org/x/tools/imports/fix.go @@ -0,0 +1,1259 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package imports + +import ( + "bytes" + "context" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/token" + "io/ioutil" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "sort" + "strconv" + "strings" + "sync" + "time" + "unicode" + "unicode/utf8" + + "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/go/packages" + "golang.org/x/tools/internal/gopathwalk" +) + +// Debug controls verbose logging. +var Debug = false + +// LocalPrefix is a comma-separated string of import path prefixes, which, if +// set, instructs Process to sort the import paths with the given prefixes +// into another group after 3rd-party packages. +var LocalPrefix string + +func localPrefixes() []string { + if LocalPrefix != "" { + return strings.Split(LocalPrefix, ",") + } + return nil +} + +// importToGroup is a list of functions which map from an import path to +// a group number. +var importToGroup = []func(importPath string) (num int, ok bool){ + func(importPath string) (num int, ok bool) { + for _, p := range localPrefixes() { + if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath { + return 3, true + } + } + return + }, + func(importPath string) (num int, ok bool) { + if strings.HasPrefix(importPath, "appengine") { + return 2, true + } + return + }, + func(importPath string) (num int, ok bool) { + if strings.Contains(importPath, ".") { + return 1, true + } + return + }, +} + +func importGroup(importPath string) int { + for _, fn := range importToGroup { + if n, ok := fn(importPath); ok { + return n + } + } + return 0 +} + +// An importInfo represents a single import statement. +type importInfo struct { + importPath string // import path, e.g. "crypto/rand". + name string // import name, e.g. "crand", or "" if none. +} + +// A packageInfo represents what's known about a package. +type packageInfo struct { + name string // real package name, if known. + exports map[string]bool // known exports. +} + +// parseOtherFiles parses all the Go files in srcDir except filename, including +// test files if filename looks like a test. +func parseOtherFiles(fset *token.FileSet, srcDir, filename string) []*ast.File { + // This could use go/packages but it doesn't buy much, and it fails + // with https://golang.org/issue/26296 in LoadFiles mode in some cases. + considerTests := strings.HasSuffix(filename, "_test.go") + + fileBase := filepath.Base(filename) + packageFileInfos, err := ioutil.ReadDir(srcDir) + if err != nil { + return nil + } + + var files []*ast.File + for _, fi := range packageFileInfos { + if fi.Name() == fileBase || !strings.HasSuffix(fi.Name(), ".go") { + continue + } + if !considerTests && strings.HasSuffix(fi.Name(), "_test.go") { + continue + } + + f, err := parser.ParseFile(fset, filepath.Join(srcDir, fi.Name()), nil, 0) + if err != nil { + continue + } + + files = append(files, f) + } + + return files +} + +// addGlobals puts the names of package vars into the provided map. +func addGlobals(f *ast.File, globals map[string]bool) { + for _, decl := range f.Decls { + genDecl, ok := decl.(*ast.GenDecl) + if !ok { + continue + } + + for _, spec := range genDecl.Specs { + valueSpec, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + globals[valueSpec.Names[0].Name] = true + } + } +} + +// collectReferences builds a map of selector expressions, from +// left hand side (X) to a set of right hand sides (Sel). +func collectReferences(f *ast.File) references { + refs := references{} + + var visitor visitFn + visitor = func(node ast.Node) ast.Visitor { + if node == nil { + return visitor + } + switch v := node.(type) { + case *ast.SelectorExpr: + xident, ok := v.X.(*ast.Ident) + if !ok { + break + } + if xident.Obj != nil { + // If the parser can resolve it, it's not a package ref. + break + } + if !ast.IsExported(v.Sel.Name) { + // Whatever this is, it's not exported from a package. + break + } + pkgName := xident.Name + r := refs[pkgName] + if r == nil { + r = make(map[string]bool) + refs[pkgName] = r + } + r[v.Sel.Name] = true + } + return visitor + } + ast.Walk(visitor, f) + return refs +} + +// collectImports returns all the imports in f, keyed by their package name as +// determined by pathToName. Unnamed imports (., _) and "C" are ignored. +func collectImports(f *ast.File) []*importInfo { + var imports []*importInfo + for _, imp := range f.Imports { + var name string + if imp.Name != nil { + name = imp.Name.Name + } + if imp.Path.Value == `"C"` || name == "_" || name == "." { + continue + } + path := strings.Trim(imp.Path.Value, `"`) + imports = append(imports, &importInfo{ + name: name, + importPath: path, + }) + } + return imports +} + +// findMissingImport searches pass's candidates for an import that provides +// pkg, containing all of syms. +func (p *pass) findMissingImport(pkg string, syms map[string]bool) *importInfo { + for _, candidate := range p.candidates { + pkgInfo, ok := p.knownPackages[candidate.importPath] + if !ok { + continue + } + if p.importIdentifier(candidate) != pkg { + continue + } + + allFound := true + for right := range syms { + if !pkgInfo.exports[right] { + allFound = false + break + } + } + + if allFound { + return candidate + } + } + return nil +} + +// references is set of references found in a Go file. The first map key is the +// left hand side of a selector expression, the second key is the right hand +// side, and the value should always be true. +type references map[string]map[string]bool + +// A pass contains all the inputs and state necessary to fix a file's imports. +// It can be modified in some ways during use; see comments below. +type pass struct { + // Inputs. These must be set before a call to load, and not modified after. + fset *token.FileSet // fset used to parse f and its siblings. + f *ast.File // the file being fixed. + srcDir string // the directory containing f. + fixEnv *fixEnv // the environment to use for go commands, etc. + loadRealPackageNames bool // if true, load package names from disk rather than guessing them. + otherFiles []*ast.File // sibling files. + + // Intermediate state, generated by load. + existingImports map[string]*importInfo + allRefs references + missingRefs references + + // Inputs to fix. These can be augmented between successive fix calls. + lastTry bool // indicates that this is the last call and fix should clean up as best it can. + candidates []*importInfo // candidate imports in priority order. + knownPackages map[string]*packageInfo // information about all known packages. +} + +// loadPackageNames saves the package names for everything referenced by imports. +func (p *pass) loadPackageNames(imports []*importInfo) error { + var unknown []string + for _, imp := range imports { + if _, ok := p.knownPackages[imp.importPath]; ok { + continue + } + unknown = append(unknown, imp.importPath) + } + + names, err := p.fixEnv.getResolver().loadPackageNames(unknown, p.srcDir) + if err != nil { + return err + } + + for path, name := range names { + p.knownPackages[path] = &packageInfo{ + name: name, + exports: map[string]bool{}, + } + } + return nil +} + +// importIdentifier returns the identifier that imp will introduce. It will +// guess if the package name has not been loaded, e.g. because the source +// is not available. +func (p *pass) importIdentifier(imp *importInfo) string { + if imp.name != "" { + return imp.name + } + known := p.knownPackages[imp.importPath] + if known != nil && known.name != "" { + return known.name + } + return importPathToAssumedName(imp.importPath) +} + +// load reads in everything necessary to run a pass, and reports whether the +// file already has all the imports it needs. It fills in p.missingRefs with the +// file's missing symbols, if any, or removes unused imports if not. +func (p *pass) load() bool { + p.knownPackages = map[string]*packageInfo{} + p.missingRefs = references{} + p.existingImports = map[string]*importInfo{} + + // Load basic information about the file in question. + p.allRefs = collectReferences(p.f) + + // Load stuff from other files in the same package: + // global variables so we know they don't need resolving, and imports + // that we might want to mimic. + globals := map[string]bool{} + for _, otherFile := range p.otherFiles { + // Don't load globals from files that are in the same directory + // but a different package. Using them to suggest imports is OK. + if p.f.Name.Name == otherFile.Name.Name { + addGlobals(otherFile, globals) + } + p.candidates = append(p.candidates, collectImports(otherFile)...) + } + + // Resolve all the import paths we've seen to package names, and store + // f's imports by the identifier they introduce. + imports := collectImports(p.f) + if p.loadRealPackageNames { + err := p.loadPackageNames(append(imports, p.candidates...)) + if err != nil { + if Debug { + log.Printf("loading package names: %v", err) + } + return false + } + } + for _, imp := range imports { + p.existingImports[p.importIdentifier(imp)] = imp + } + + // Find missing references. + for left, rights := range p.allRefs { + if globals[left] { + continue + } + _, ok := p.existingImports[left] + if !ok { + p.missingRefs[left] = rights + continue + } + } + if len(p.missingRefs) != 0 { + return false + } + + return p.fix() +} + +// fix attempts to satisfy missing imports using p.candidates. If it finds +// everything, or if p.lastTry is true, it adds the imports it found, +// removes anything unused, and returns true. +func (p *pass) fix() bool { + // Find missing imports. + var selected []*importInfo + for left, rights := range p.missingRefs { + if imp := p.findMissingImport(left, rights); imp != nil { + selected = append(selected, imp) + } + } + + if !p.lastTry && len(selected) != len(p.missingRefs) { + return false + } + + // Found everything, or giving up. Add the new imports and remove any unused. + for _, imp := range p.existingImports { + // We deliberately ignore globals here, because we can't be sure + // they're in the same package. People do things like put multiple + // main packages in the same directory, and we don't want to + // remove imports if they happen to have the same name as a var in + // a different package. + if _, ok := p.allRefs[p.importIdentifier(imp)]; !ok { + astutil.DeleteNamedImport(p.fset, p.f, imp.name, imp.importPath) + } + } + + for _, imp := range selected { + astutil.AddNamedImport(p.fset, p.f, imp.name, imp.importPath) + } + + if p.loadRealPackageNames { + for _, imp := range p.f.Imports { + if imp.Name != nil { + continue + } + path := strings.Trim(imp.Path.Value, `""`) + ident := p.importIdentifier(&importInfo{importPath: path}) + if ident != importPathToAssumedName(path) { + imp.Name = &ast.Ident{Name: ident, NamePos: imp.Pos()} + } + } + } + + return true +} + +// assumeSiblingImportsValid assumes that siblings' use of packages is valid, +// adding the exports they use. +func (p *pass) assumeSiblingImportsValid() { + for _, f := range p.otherFiles { + refs := collectReferences(f) + imports := collectImports(f) + importsByName := map[string]*importInfo{} + for _, imp := range imports { + importsByName[p.importIdentifier(imp)] = imp + } + for left, rights := range refs { + if imp, ok := importsByName[left]; ok { + if _, ok := stdlib[imp.importPath]; ok { + // We have the stdlib in memory; no need to guess. + rights = stdlib[imp.importPath] + } + p.addCandidate(imp, &packageInfo{ + // no name; we already know it. + exports: rights, + }) + } + } + } +} + +// addCandidate adds a candidate import to p, and merges in the information +// in pkg. +func (p *pass) addCandidate(imp *importInfo, pkg *packageInfo) { + p.candidates = append(p.candidates, imp) + if existing, ok := p.knownPackages[imp.importPath]; ok { + if existing.name == "" { + existing.name = pkg.name + } + for export := range pkg.exports { + existing.exports[export] = true + } + } else { + p.knownPackages[imp.importPath] = pkg + } +} + +// fixImports adds and removes imports from f so that all its references are +// satisfied and there are no unused imports. +// +// This is declared as a variable rather than a function so goimports can +// easily be extended by adding a file with an init function. +var fixImports = fixImportsDefault + +func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *fixEnv) error { + abs, err := filepath.Abs(filename) + if err != nil { + return err + } + srcDir := filepath.Dir(abs) + if Debug { + log.Printf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir) + } + + // First pass: looking only at f, and using the naive algorithm to + // derive package names from import paths, see if the file is already + // complete. We can't add any imports yet, because we don't know + // if missing references are actually package vars. + p := &pass{fset: fset, f: f, srcDir: srcDir} + if p.load() { + return nil + } + + otherFiles := parseOtherFiles(fset, srcDir, filename) + + // Second pass: add information from other files in the same package, + // like their package vars and imports. + p.otherFiles = otherFiles + if p.load() { + return nil + } + + // Now we can try adding imports from the stdlib. + p.assumeSiblingImportsValid() + addStdlibCandidates(p, p.missingRefs) + if p.fix() { + return nil + } + + // Third pass: get real package names where we had previously used + // the naive algorithm. This is the first step that will use the + // environment, so we provide it here for the first time. + p = &pass{fset: fset, f: f, srcDir: srcDir, fixEnv: env} + p.loadRealPackageNames = true + p.otherFiles = otherFiles + if p.load() { + return nil + } + + addStdlibCandidates(p, p.missingRefs) + p.assumeSiblingImportsValid() + if p.fix() { + return nil + } + + // Go look for candidates in $GOPATH, etc. We don't necessarily load + // the real exports of sibling imports, so keep assuming their contents. + if err := addExternalCandidates(p, p.missingRefs, filename); err != nil { + return err + } + + p.lastTry = true + p.fix() + return nil +} + +// fixEnv contains environment variables and settings that affect the use of +// the go command, the go/build package, etc. +type fixEnv struct { + // If non-empty, these will be used instead of the + // process-wide values. + GOPATH, GOROOT, GO111MODULE, GOPROXY, GOFLAGS string + WorkingDir string + + // If true, use go/packages regardless of the environment. + ForceGoPackages bool + + resolver resolver +} + +func (e *fixEnv) env() []string { + env := os.Environ() + add := func(k, v string) { + if v != "" { + env = append(env, k+"="+v) + } + } + add("GOPATH", e.GOPATH) + add("GOROOT", e.GOROOT) + add("GO111MODULE", e.GO111MODULE) + add("GOPROXY", e.GOPROXY) + add("GOFLAGS", e.GOFLAGS) + if e.WorkingDir != "" { + add("PWD", e.WorkingDir) + } + return env +} + +func (e *fixEnv) getResolver() resolver { + if e.resolver != nil { + return e.resolver + } + if e.ForceGoPackages { + return &goPackagesResolver{env: e} + } + + out, err := e.invokeGo("env", "GOMOD") + if err != nil || len(bytes.TrimSpace(out.Bytes())) == 0 { + return &gopathResolver{env: e} + } + return &moduleResolver{env: e} +} + +func (e *fixEnv) newPackagesConfig(mode packages.LoadMode) *packages.Config { + return &packages.Config{ + Mode: mode, + Dir: e.WorkingDir, + Env: e.env(), + } +} + +func (e *fixEnv) buildContext() *build.Context { + ctx := build.Default + ctx.GOROOT = e.GOROOT + ctx.GOPATH = e.GOPATH + return &ctx +} + +func (e *fixEnv) invokeGo(args ...string) (*bytes.Buffer, error) { + cmd := exec.Command("go", args...) + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + cmd.Stdout = stdout + cmd.Stderr = stderr + cmd.Env = e.env() + cmd.Dir = e.WorkingDir + + if Debug { + defer func(start time.Time) { log.Printf("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now()) + } + if err := cmd.Run(); err != nil { + return nil, fmt.Errorf("running go: %v (stderr:\n%s)", err, stderr) + } + return stdout, nil +} + +func cmdDebugStr(cmd *exec.Cmd) string { + env := make(map[string]string) + for _, kv := range cmd.Env { + split := strings.Split(kv, "=") + k, v := split[0], split[1] + env[k] = v + } + + return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v go %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], cmd.Args) +} + +func addStdlibCandidates(pass *pass, refs references) { + add := func(pkg string) { + pass.addCandidate( + &importInfo{importPath: pkg}, + &packageInfo{name: path.Base(pkg), exports: stdlib[pkg]}) + } + for left := range refs { + if left == "rand" { + // Make sure we try crypto/rand before math/rand. + add("crypto/rand") + add("math/rand") + continue + } + for importPath := range stdlib { + if path.Base(importPath) == left { + add(importPath) + } + } + } +} + +// A resolver does the build-system-specific parts of goimports. +type resolver interface { + // loadPackageNames loads the package names in importPaths. + loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) + // scan finds (at least) the packages satisfying refs. The returned slice is unordered. + scan(refs references) ([]*pkg, error) +} + +// gopathResolver implements resolver for GOPATH and module workspaces using go/packages. +type goPackagesResolver struct { + env *fixEnv +} + +func (r *goPackagesResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { + cfg := r.env.newPackagesConfig(packages.LoadFiles) + pkgs, err := packages.Load(cfg, importPaths...) + if err != nil { + return nil, err + } + names := map[string]string{} + for _, pkg := range pkgs { + names[VendorlessPath(pkg.PkgPath)] = pkg.Name + } + // We may not have found all the packages. Guess the rest. + for _, path := range importPaths { + if _, ok := names[path]; ok { + continue + } + names[path] = importPathToAssumedName(path) + } + return names, nil + +} + +func (r *goPackagesResolver) scan(refs references) ([]*pkg, error) { + var loadQueries []string + for pkgName := range refs { + loadQueries = append(loadQueries, "iamashamedtousethedisabledqueryname="+pkgName) + } + sort.Strings(loadQueries) + cfg := r.env.newPackagesConfig(packages.LoadFiles) + goPackages, err := packages.Load(cfg, loadQueries...) + if err != nil { + return nil, err + } + + var scan []*pkg + for _, goPackage := range goPackages { + scan = append(scan, &pkg{ + dir: filepath.Dir(goPackage.CompiledGoFiles[0]), + importPathShort: VendorlessPath(goPackage.PkgPath), + goPackage: goPackage, + }) + } + return scan, nil +} + +func addExternalCandidates(pass *pass, refs references, filename string) error { + dirScan, err := pass.fixEnv.getResolver().scan(refs) + if err != nil { + return err + } + + // Search for imports matching potential package references. + type result struct { + imp *importInfo + pkg *packageInfo + } + results := make(chan result, len(refs)) + + ctx, cancel := context.WithCancel(context.TODO()) + var wg sync.WaitGroup + defer func() { + cancel() + wg.Wait() + }() + var ( + firstErr error + firstErrOnce sync.Once + ) + for pkgName, symbols := range refs { + wg.Add(1) + go func(pkgName string, symbols map[string]bool) { + defer wg.Done() + + found, err := findImport(ctx, pass.fixEnv, dirScan, pkgName, symbols, filename) + + if err != nil { + firstErrOnce.Do(func() { + firstErr = err + cancel() + }) + return + } + + if found == nil { + return // No matching package. + } + + imp := &importInfo{ + importPath: found.importPathShort, + } + + pkg := &packageInfo{ + name: pkgName, + exports: symbols, + } + results <- result{imp, pkg} + }(pkgName, symbols) + } + go func() { + wg.Wait() + close(results) + }() + + for result := range results { + pass.addCandidate(result.imp, result.pkg) + } + return firstErr +} + +// notIdentifier reports whether ch is an invalid identifier character. +func notIdentifier(ch rune) bool { + return !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || + '0' <= ch && ch <= '9' || + ch == '_' || + ch >= utf8.RuneSelf && (unicode.IsLetter(ch) || unicode.IsDigit(ch))) +} + +// importPathToAssumedName returns the assumed package name of an import path. +// It does this using only string parsing of the import path. +// It picks the last element of the path that does not look like a major +// version, and then picks the valid identifier off the start of that element. +// It is used to determine if a local rename should be added to an import for +// clarity. +// This function could be moved to a standard package and exported if we want +// for use in other tools. +func importPathToAssumedName(importPath string) string { + base := path.Base(importPath) + if strings.HasPrefix(base, "v") { + if _, err := strconv.Atoi(base[1:]); err == nil { + dir := path.Dir(importPath) + if dir != "." { + base = path.Base(dir) + } + } + } + base = strings.TrimPrefix(base, "go-") + if i := strings.IndexFunc(base, notIdentifier); i >= 0 { + base = base[:i] + } + return base +} + +// gopathResolver implements resolver for GOPATH workspaces. +type gopathResolver struct { + env *fixEnv +} + +func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { + names := map[string]string{} + for _, path := range importPaths { + names[path] = importPathToName(r.env, path, srcDir) + } + return names, nil +} + +// importPathToNameGoPath finds out the actual package name, as declared in its .go files. +// If there's a problem, it returns "". +func importPathToName(env *fixEnv, importPath, srcDir string) (packageName string) { + // Fast path for standard library without going to disk. + if _, ok := stdlib[importPath]; ok { + return path.Base(importPath) // stdlib packages always match their paths. + } + + buildPkg, err := env.buildContext().Import(importPath, srcDir, build.FindOnly) + if err != nil { + return "" + } + pkgName, err := packageDirToName(buildPkg.Dir) + if err != nil { + return "" + } + return pkgName +} + +// packageDirToName is a faster version of build.Import if +// the only thing desired is the package name. It uses build.FindOnly +// to find the directory and then only parses one file in the package, +// trusting that the files in the directory are consistent. +func packageDirToName(dir string) (packageName string, err error) { + d, err := os.Open(dir) + if err != nil { + return "", err + } + names, err := d.Readdirnames(-1) + d.Close() + if err != nil { + return "", err + } + sort.Strings(names) // to have predictable behavior + var lastErr error + var nfile int + for _, name := range names { + if !strings.HasSuffix(name, ".go") { + continue + } + if strings.HasSuffix(name, "_test.go") { + continue + } + nfile++ + fullFile := filepath.Join(dir, name) + + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, fullFile, nil, parser.PackageClauseOnly) + if err != nil { + lastErr = err + continue + } + pkgName := f.Name.Name + if pkgName == "documentation" { + // Special case from go/build.ImportDir, not + // handled by ctx.MatchFile. + continue + } + if pkgName == "main" { + // Also skip package main, assuming it's a +build ignore generator or example. + // Since you can't import a package main anyway, there's no harm here. + continue + } + return pkgName, nil + } + if lastErr != nil { + return "", lastErr + } + return "", fmt.Errorf("no importable package found in %d Go files", nfile) +} + +type pkg struct { + goPackage *packages.Package + dir string // absolute file path to pkg directory ("/usr/lib/go/src/net/http") + importPathShort string // vendorless import path ("net/http", "a/b") +} + +type pkgDistance struct { + pkg *pkg + distance int // relative distance to target +} + +// byDistanceOrImportPathShortLength sorts by relative distance breaking ties +// on the short import path length and then the import string itself. +type byDistanceOrImportPathShortLength []pkgDistance + +func (s byDistanceOrImportPathShortLength) Len() int { return len(s) } +func (s byDistanceOrImportPathShortLength) Less(i, j int) bool { + di, dj := s[i].distance, s[j].distance + if di == -1 { + return false + } + if dj == -1 { + return true + } + if di != dj { + return di < dj + } + + vi, vj := s[i].pkg.importPathShort, s[j].pkg.importPathShort + if len(vi) != len(vj) { + return len(vi) < len(vj) + } + return vi < vj +} +func (s byDistanceOrImportPathShortLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func distance(basepath, targetpath string) int { + p, err := filepath.Rel(basepath, targetpath) + if err != nil { + return -1 + } + if p == "." { + return 0 + } + return strings.Count(p, string(filepath.Separator)) + 1 +} + +func (r *gopathResolver) scan(_ references) ([]*pkg, error) { + dupCheck := make(map[string]bool) + var result []*pkg + + var mu sync.Mutex + + add := func(root gopathwalk.Root, dir string) { + mu.Lock() + defer mu.Unlock() + + if _, dup := dupCheck[dir]; dup { + return + } + dupCheck[dir] = true + importpath := filepath.ToSlash(dir[len(root.Path)+len("/"):]) + result = append(result, &pkg{ + importPathShort: VendorlessPath(importpath), + dir: dir, + }) + } + gopathwalk.Walk(gopathwalk.SrcDirsRoots(r.env.buildContext()), add, gopathwalk.Options{Debug: Debug, ModulesEnabled: false}) + return result, nil +} + +// VendorlessPath returns the devendorized version of the import path ipath. +// For example, VendorlessPath("foo/bar/vendor/a/b") returns "a/b". +func VendorlessPath(ipath string) string { + // Devendorize for use in import statement. + if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 { + return ipath[i+len("/vendor/"):] + } + if strings.HasPrefix(ipath, "vendor/") { + return ipath[len("vendor/"):] + } + return ipath +} + +// loadExports returns the set of exported symbols in the package at dir. +// It returns nil on error or if the package name in dir does not match expectPackage. +func loadExports(ctx context.Context, env *fixEnv, expectPackage string, pkg *pkg) (map[string]bool, error) { + if Debug { + log.Printf("loading exports in dir %s (seeking package %s)", pkg.dir, expectPackage) + } + if pkg.goPackage != nil { + exports := map[string]bool{} + fset := token.NewFileSet() + for _, fname := range pkg.goPackage.CompiledGoFiles { + f, err := parser.ParseFile(fset, fname, nil, 0) + if err != nil { + return nil, fmt.Errorf("parsing %s: %v", fname, err) + } + for name := range f.Scope.Objects { + if ast.IsExported(name) { + exports[name] = true + } + } + } + return exports, nil + } + + exports := make(map[string]bool) + + // Look for non-test, buildable .go files which could provide exports. + all, err := ioutil.ReadDir(pkg.dir) + if err != nil { + return nil, err + } + var files []os.FileInfo + for _, fi := range all { + name := fi.Name() + if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") { + continue + } + match, err := env.buildContext().MatchFile(pkg.dir, fi.Name()) + if err != nil || !match { + continue + } + files = append(files, fi) + } + + if len(files) == 0 { + return nil, fmt.Errorf("dir %v contains no buildable, non-test .go files", pkg.dir) + } + + fset := token.NewFileSet() + for _, fi := range files { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + fullFile := filepath.Join(pkg.dir, fi.Name()) + f, err := parser.ParseFile(fset, fullFile, nil, 0) + if err != nil { + return nil, fmt.Errorf("parsing %s: %v", fullFile, err) + } + pkgName := f.Name.Name + if pkgName == "documentation" { + // Special case from go/build.ImportDir, not + // handled by MatchFile above. + continue + } + if pkgName != expectPackage { + return nil, fmt.Errorf("scan of dir %v is not expected package %v (actually %v)", pkg.dir, expectPackage, pkgName) + } + for name := range f.Scope.Objects { + if ast.IsExported(name) { + exports[name] = true + } + } + } + + if Debug { + exportList := make([]string, 0, len(exports)) + for k := range exports { + exportList = append(exportList, k) + } + sort.Strings(exportList) + log.Printf("loaded exports in dir %v (package %v): %v", pkg.dir, expectPackage, strings.Join(exportList, ", ")) + } + return exports, nil +} + +// findImport searches for a package with the given symbols. +// If no package is found, findImport returns ("", false, nil) +func findImport(ctx context.Context, env *fixEnv, dirScan []*pkg, pkgName string, symbols map[string]bool, filename string) (*pkg, error) { + pkgDir, err := filepath.Abs(filename) + if err != nil { + return nil, err + } + pkgDir = filepath.Dir(pkgDir) + + // Find candidate packages, looking only at their directory names first. + var candidates []pkgDistance + for _, pkg := range dirScan { + if pkg.dir != pkgDir && pkgIsCandidate(filename, pkgName, pkg) { + candidates = append(candidates, pkgDistance{ + pkg: pkg, + distance: distance(pkgDir, pkg.dir), + }) + } + } + + // Sort the candidates by their import package length, + // assuming that shorter package names are better than long + // ones. Note that this sorts by the de-vendored name, so + // there's no "penalty" for vendoring. + sort.Sort(byDistanceOrImportPathShortLength(candidates)) + if Debug { + for i, c := range candidates { + log.Printf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir) + } + } + + // Collect exports for packages with matching names. + + rescv := make([]chan *pkg, len(candidates)) + for i := range candidates { + rescv[i] = make(chan *pkg, 1) + } + const maxConcurrentPackageImport = 4 + loadExportsSem := make(chan struct{}, maxConcurrentPackageImport) + + ctx, cancel := context.WithCancel(ctx) + var wg sync.WaitGroup + defer func() { + cancel() + wg.Wait() + }() + + wg.Add(1) + go func() { + defer wg.Done() + for i, c := range candidates { + select { + case loadExportsSem <- struct{}{}: + case <-ctx.Done(): + return + } + + wg.Add(1) + go func(c pkgDistance, resc chan<- *pkg) { + defer func() { + <-loadExportsSem + wg.Done() + }() + + exports, err := loadExports(ctx, env, pkgName, c.pkg) + if err != nil { + if Debug { + log.Printf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err) + } + resc <- nil + return + } + + // If it doesn't have the right + // symbols, send nil to mean no match. + for symbol := range symbols { + if !exports[symbol] { + resc <- nil + return + } + } + resc <- c.pkg + }(c, rescv[i]) + } + }() + + for _, resc := range rescv { + pkg := <-resc + if pkg == nil { + continue + } + return pkg, nil + } + return nil, nil +} + +// pkgIsCandidate reports whether pkg is a candidate for satisfying the +// finding which package pkgIdent in the file named by filename is trying +// to refer to. +// +// This check is purely lexical and is meant to be as fast as possible +// because it's run over all $GOPATH directories to filter out poor +// candidates in order to limit the CPU and I/O later parsing the +// exports in candidate packages. +// +// filename is the file being formatted. +// pkgIdent is the package being searched for, like "client" (if +// searching for "client.New") +func pkgIsCandidate(filename, pkgIdent string, pkg *pkg) bool { + // Check "internal" and "vendor" visibility: + if !canUse(filename, pkg.dir) { + return false + } + + // Speed optimization to minimize disk I/O: + // the last two components on disk must contain the + // package name somewhere. + // + // This permits mismatch naming like directory + // "go-foo" being package "foo", or "pkg.v3" being "pkg", + // or directory "google.golang.org/api/cloudbilling/v1" + // being package "cloudbilling", but doesn't + // permit a directory "foo" to be package + // "bar", which is strongly discouraged + // anyway. There's no reason goimports needs + // to be slow just to accommodate that. + lastTwo := lastTwoComponents(pkg.importPathShort) + if strings.Contains(lastTwo, pkgIdent) { + return true + } + if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(pkgIdent) { + lastTwo = lowerASCIIAndRemoveHyphen(lastTwo) + if strings.Contains(lastTwo, pkgIdent) { + return true + } + } + + return false +} + +func hasHyphenOrUpperASCII(s string) bool { + for i := 0; i < len(s); i++ { + b := s[i] + if b == '-' || ('A' <= b && b <= 'Z') { + return true + } + } + return false +} + +func lowerASCIIAndRemoveHyphen(s string) (ret string) { + buf := make([]byte, 0, len(s)) + for i := 0; i < len(s); i++ { + b := s[i] + switch { + case b == '-': + continue + case 'A' <= b && b <= 'Z': + buf = append(buf, b+('a'-'A')) + default: + buf = append(buf, b) + } + } + return string(buf) +} + +// canUse reports whether the package in dir is usable from filename, +// respecting the Go "internal" and "vendor" visibility rules. +func canUse(filename, dir string) bool { + // Fast path check, before any allocations. If it doesn't contain vendor + // or internal, it's not tricky: + // Note that this can false-negative on directories like "notinternal", + // but we check it correctly below. This is just a fast path. + if !strings.Contains(dir, "vendor") && !strings.Contains(dir, "internal") { + return true + } + + dirSlash := filepath.ToSlash(dir) + if !strings.Contains(dirSlash, "/vendor/") && !strings.Contains(dirSlash, "/internal/") && !strings.HasSuffix(dirSlash, "/internal") { + return true + } + // Vendor or internal directory only visible from children of parent. + // That means the path from the current directory to the target directory + // can contain ../vendor or ../internal but not ../foo/vendor or ../foo/internal + // or bar/vendor or bar/internal. + // After stripping all the leading ../, the only okay place to see vendor or internal + // is at the very beginning of the path. + absfile, err := filepath.Abs(filename) + if err != nil { + return false + } + absdir, err := filepath.Abs(dir) + if err != nil { + return false + } + rel, err := filepath.Rel(absfile, absdir) + if err != nil { + return false + } + relSlash := filepath.ToSlash(rel) + if i := strings.LastIndex(relSlash, "../"); i >= 0 { + relSlash = relSlash[i+len("../"):] + } + return !strings.Contains(relSlash, "/vendor/") && !strings.Contains(relSlash, "/internal/") && !strings.HasSuffix(relSlash, "/internal") +} + +// lastTwoComponents returns at most the last two path components +// of v, using either / or \ as the path separator. +func lastTwoComponents(v string) string { + nslash := 0 + for i := len(v) - 1; i >= 0; i-- { + if v[i] == '/' || v[i] == '\\' { + nslash++ + if nslash == 2 { + return v[i:] + } + } + } + return v +} + +type visitFn func(node ast.Node) ast.Visitor + +func (fn visitFn) Visit(node ast.Node) ast.Visitor { + return fn(node) +} diff --git a/vendor/golang.org/x/tools/imports/imports.go b/vendor/golang.org/x/tools/imports/imports.go new file mode 100644 index 0000000000..07101cb804 --- /dev/null +++ b/vendor/golang.org/x/tools/imports/imports.go @@ -0,0 +1,315 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run mkstdlib.go + +// Package imports implements a Go pretty-printer (like package "go/format") +// that also adds or removes import statements as necessary. +package imports // import "golang.org/x/tools/imports" + +import ( + "bufio" + "bytes" + "fmt" + "go/ast" + "go/build" + "go/format" + "go/parser" + "go/printer" + "go/token" + "io" + "io/ioutil" + "regexp" + "strconv" + "strings" + + "golang.org/x/tools/go/ast/astutil" +) + +// Options specifies options for processing files. +type Options struct { + Fragment bool // Accept fragment of a source file (no package statement) + AllErrors bool // Report all errors (not just the first 10 on different lines) + + Comments bool // Print comments (true if nil *Options provided) + TabIndent bool // Use tabs for indent (true if nil *Options provided) + TabWidth int // Tab width (8 if nil *Options provided) + + FormatOnly bool // Disable the insertion and deletion of imports +} + +// Process formats and adjusts imports for the provided file. +// If opt is nil the defaults are used. +// +// Note that filename's directory influences which imports can be chosen, +// so it is important that filename be accurate. +// To process data ``as if'' it were in filename, pass the data as a non-nil src. +func Process(filename string, src []byte, opt *Options) ([]byte, error) { + env := &fixEnv{GOPATH: build.Default.GOPATH, GOROOT: build.Default.GOROOT} + return process(filename, src, opt, env) +} + +func process(filename string, src []byte, opt *Options, env *fixEnv) ([]byte, error) { + if opt == nil { + opt = &Options{Comments: true, TabIndent: true, TabWidth: 8} + } + if src == nil { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + src = b + } + + fileSet := token.NewFileSet() + file, adjust, err := parse(fileSet, filename, src, opt) + if err != nil { + return nil, err + } + + if !opt.FormatOnly { + if err := fixImports(fileSet, file, filename, env); err != nil { + return nil, err + } + } + + sortImports(fileSet, file) + imps := astutil.Imports(fileSet, file) + var spacesBefore []string // import paths we need spaces before + for _, impSection := range imps { + // Within each block of contiguous imports, see if any + // import lines are in different group numbers. If so, + // we'll need to put a space between them so it's + // compatible with gofmt. + lastGroup := -1 + for _, importSpec := range impSection { + importPath, _ := strconv.Unquote(importSpec.Path.Value) + groupNum := importGroup(importPath) + if groupNum != lastGroup && lastGroup != -1 { + spacesBefore = append(spacesBefore, importPath) + } + lastGroup = groupNum + } + + } + + printerMode := printer.UseSpaces + if opt.TabIndent { + printerMode |= printer.TabIndent + } + printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth} + + var buf bytes.Buffer + err = printConfig.Fprint(&buf, fileSet, file) + if err != nil { + return nil, err + } + out := buf.Bytes() + if adjust != nil { + out = adjust(src, out) + } + if len(spacesBefore) > 0 { + out, err = addImportSpaces(bytes.NewReader(out), spacesBefore) + if err != nil { + return nil, err + } + } + + out, err = format.Source(out) + if err != nil { + return nil, err + } + return out, nil +} + +// parse parses src, which was read from filename, +// as a Go source file or statement list. +func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) { + parserMode := parser.Mode(0) + if opt.Comments { + parserMode |= parser.ParseComments + } + if opt.AllErrors { + parserMode |= parser.AllErrors + } + + // Try as whole source file. + file, err := parser.ParseFile(fset, filename, src, parserMode) + if err == nil { + return file, nil, nil + } + // If the error is that the source file didn't begin with a + // package line and we accept fragmented input, fall through to + // try as a source fragment. Stop and return on any other error. + if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") { + return nil, nil, err + } + + // If this is a declaration list, make it a source file + // by inserting a package clause. + // Insert using a ;, not a newline, so that parse errors are on + // the correct line. + const prefix = "package main;" + psrc := append([]byte(prefix), src...) + file, err = parser.ParseFile(fset, filename, psrc, parserMode) + if err == nil { + // Gofmt will turn the ; into a \n. + // Do that ourselves now and update the file contents, + // so that positions and line numbers are correct going forward. + psrc[len(prefix)-1] = '\n' + fset.File(file.Package).SetLinesForContent(psrc) + + // If a main function exists, we will assume this is a main + // package and leave the file. + if containsMainFunc(file) { + return file, nil, nil + } + + adjust := func(orig, src []byte) []byte { + // Remove the package clause. + src = src[len(prefix):] + return matchSpace(orig, src) + } + return file, adjust, nil + } + // If the error is that the source file didn't begin with a + // declaration, fall through to try as a statement list. + // Stop and return on any other error. + if !strings.Contains(err.Error(), "expected declaration") { + return nil, nil, err + } + + // If this is a statement list, make it a source file + // by inserting a package clause and turning the list + // into a function body. This handles expressions too. + // Insert using a ;, not a newline, so that the line numbers + // in fsrc match the ones in src. + fsrc := append(append([]byte("package p; func _() {"), src...), '}') + file, err = parser.ParseFile(fset, filename, fsrc, parserMode) + if err == nil { + adjust := func(orig, src []byte) []byte { + // Remove the wrapping. + // Gofmt has turned the ; into a \n\n. + src = src[len("package p\n\nfunc _() {"):] + src = src[:len(src)-len("}\n")] + // Gofmt has also indented the function body one level. + // Remove that indent. + src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1) + return matchSpace(orig, src) + } + return file, adjust, nil + } + + // Failed, and out of options. + return nil, nil, err +} + +// containsMainFunc checks if a file contains a function declaration with the +// function signature 'func main()' +func containsMainFunc(file *ast.File) bool { + for _, decl := range file.Decls { + if f, ok := decl.(*ast.FuncDecl); ok { + if f.Name.Name != "main" { + continue + } + + if len(f.Type.Params.List) != 0 { + continue + } + + if f.Type.Results != nil && len(f.Type.Results.List) != 0 { + continue + } + + return true + } + } + + return false +} + +func cutSpace(b []byte) (before, middle, after []byte) { + i := 0 + for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') { + i++ + } + j := len(b) + for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') { + j-- + } + if i <= j { + return b[:i], b[i:j], b[j:] + } + return nil, nil, b[j:] +} + +// matchSpace reformats src to use the same space context as orig. +// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src. +// 2) matchSpace copies the indentation of the first non-blank line in orig +// to every non-blank line in src. +// 3) matchSpace copies the trailing space from orig and uses it in place +// of src's trailing space. +func matchSpace(orig []byte, src []byte) []byte { + before, _, after := cutSpace(orig) + i := bytes.LastIndex(before, []byte{'\n'}) + before, indent := before[:i+1], before[i+1:] + + _, src, _ = cutSpace(src) + + var b bytes.Buffer + b.Write(before) + for len(src) > 0 { + line := src + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, src = line[:i+1], line[i+1:] + } else { + src = nil + } + if len(line) > 0 && line[0] != '\n' { // not blank + b.Write(indent) + } + b.Write(line) + } + b.Write(after) + return b.Bytes() +} + +var impLine = regexp.MustCompile(`^\s+(?:[\w\.]+\s+)?"(.+)"`) + +func addImportSpaces(r io.Reader, breaks []string) ([]byte, error) { + var out bytes.Buffer + in := bufio.NewReader(r) + inImports := false + done := false + for { + s, err := in.ReadString('\n') + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + + if !inImports && !done && strings.HasPrefix(s, "import") { + inImports = true + } + if inImports && (strings.HasPrefix(s, "var") || + strings.HasPrefix(s, "func") || + strings.HasPrefix(s, "const") || + strings.HasPrefix(s, "type")) { + done = true + inImports = false + } + if inImports && len(breaks) > 0 { + if m := impLine.FindStringSubmatch(s); m != nil { + if m[1] == breaks[0] { + out.WriteByte('\n') + breaks = breaks[1:] + } + } + } + + fmt.Fprint(&out, s) + } + return out.Bytes(), nil +} diff --git a/vendor/golang.org/x/tools/imports/mkindex.go b/vendor/golang.org/x/tools/imports/mkindex.go new file mode 100644 index 0000000000..755e2394f2 --- /dev/null +++ b/vendor/golang.org/x/tools/imports/mkindex.go @@ -0,0 +1,173 @@ +// +build ignore + +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Command mkindex creates the file "pkgindex.go" containing an index of the Go +// standard library. The file is intended to be built as part of the imports +// package, so that the package may be used in environments where a GOROOT is +// not available (such as App Engine). +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/build" + "go/format" + "go/parser" + "go/token" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "strings" +) + +var ( + pkgIndex = make(map[string][]pkg) + exports = make(map[string]map[string]bool) +) + +func main() { + // Don't use GOPATH. + ctx := build.Default + ctx.GOPATH = "" + + // Populate pkgIndex global from GOROOT. + for _, path := range ctx.SrcDirs() { + f, err := os.Open(path) + if err != nil { + log.Print(err) + continue + } + children, err := f.Readdir(-1) + f.Close() + if err != nil { + log.Print(err) + continue + } + for _, child := range children { + if child.IsDir() { + loadPkg(path, child.Name()) + } + } + } + // Populate exports global. + for _, ps := range pkgIndex { + for _, p := range ps { + e := loadExports(p.dir) + if e != nil { + exports[p.dir] = e + } + } + } + + // Construct source file. + var buf bytes.Buffer + fmt.Fprint(&buf, pkgIndexHead) + fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex) + fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports) + src := buf.Bytes() + + // Replace main.pkg type name with pkg. + src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1) + // Replace actual GOROOT with "/go". + src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1) + // Add some line wrapping. + src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1) + src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1) + + var err error + src, err = format.Source(src) + if err != nil { + log.Fatal(err) + } + + // Write out source file. + err = ioutil.WriteFile("pkgindex.go", src, 0644) + if err != nil { + log.Fatal(err) + } +} + +const pkgIndexHead = `package imports + +func init() { + pkgIndexOnce.Do(func() { + pkgIndex.m = pkgIndexMaster + }) + loadExports = func(dir string) map[string]bool { + return exportsMaster[dir] + } +} +` + +type pkg struct { + importpath string // full pkg import path, e.g. "net/http" + dir string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt" +} + +var fset = token.NewFileSet() + +func loadPkg(root, importpath string) { + shortName := path.Base(importpath) + if shortName == "testdata" { + return + } + + dir := filepath.Join(root, importpath) + pkgIndex[shortName] = append(pkgIndex[shortName], pkg{ + importpath: importpath, + dir: dir, + }) + + pkgDir, err := os.Open(dir) + if err != nil { + return + } + children, err := pkgDir.Readdir(-1) + pkgDir.Close() + if err != nil { + return + } + for _, child := range children { + name := child.Name() + if name == "" { + continue + } + if c := name[0]; c == '.' || ('0' <= c && c <= '9') { + continue + } + if child.IsDir() { + loadPkg(root, filepath.Join(importpath, name)) + } + } +} + +func loadExports(dir string) map[string]bool { + exports := make(map[string]bool) + buildPkg, err := build.ImportDir(dir, 0) + if err != nil { + if strings.Contains(err.Error(), "no buildable Go source files in") { + return nil + } + log.Printf("could not import %q: %v", dir, err) + return nil + } + for _, file := range buildPkg.GoFiles { + f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0) + if err != nil { + log.Printf("could not parse %q: %v", file, err) + continue + } + for name := range f.Scope.Objects { + if ast.IsExported(name) { + exports[name] = true + } + } + } + return exports +} diff --git a/vendor/golang.org/x/tools/imports/mkstdlib.go b/vendor/golang.org/x/tools/imports/mkstdlib.go new file mode 100644 index 0000000000..c8865e5559 --- /dev/null +++ b/vendor/golang.org/x/tools/imports/mkstdlib.go @@ -0,0 +1,132 @@ +// +build ignore + +// mkstdlib generates the zstdlib.go file, containing the Go standard +// library API symbols. It's baked into the binary to avoid scanning +// GOPATH in the common case. +package main + +import ( + "bufio" + "bytes" + "fmt" + "go/format" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "sort" + "strings" +) + +func mustOpen(name string) io.Reader { + f, err := os.Open(name) + if err != nil { + log.Fatal(err) + } + return f +} + +func api(base string) string { + return filepath.Join(runtime.GOROOT(), "api", base) +} + +var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`) + +var unsafeSyms = map[string]bool{"Alignof": true, "ArbitraryType": true, "Offsetof": true, "Pointer": true, "Sizeof": true} + +func main() { + var buf bytes.Buffer + outf := func(format string, args ...interface{}) { + fmt.Fprintf(&buf, format, args...) + } + outf("// Code generated by mkstdlib.go. DO NOT EDIT.\n\n") + outf("package imports\n") + outf("var stdlib = map[string]map[string]bool{\n") + f := io.MultiReader( + mustOpen(api("go1.txt")), + mustOpen(api("go1.1.txt")), + mustOpen(api("go1.2.txt")), + mustOpen(api("go1.3.txt")), + mustOpen(api("go1.4.txt")), + mustOpen(api("go1.5.txt")), + mustOpen(api("go1.6.txt")), + mustOpen(api("go1.7.txt")), + mustOpen(api("go1.8.txt")), + mustOpen(api("go1.9.txt")), + mustOpen(api("go1.10.txt")), + mustOpen(api("go1.11.txt")), + mustOpen(api("go1.12.txt")), + + // The API of the syscall/js package needs to be computed explicitly, + // because it's not included in the GOROOT/api/go1.*.txt files at this time. + syscallJSAPI(), + ) + sc := bufio.NewScanner(f) + + pkgs := map[string]map[string]bool{ + "unsafe": unsafeSyms, + } + paths := []string{"unsafe"} + + for sc.Scan() { + l := sc.Text() + has := func(v string) bool { return strings.Contains(l, v) } + if has("struct, ") || has("interface, ") || has(", method (") { + continue + } + if m := sym.FindStringSubmatch(l); m != nil { + path, sym := m[1], m[2] + + if _, ok := pkgs[path]; !ok { + pkgs[path] = map[string]bool{} + paths = append(paths, path) + } + pkgs[path][sym] = true + } + } + if err := sc.Err(); err != nil { + log.Fatal(err) + } + sort.Strings(paths) + for _, path := range paths { + outf("\t%q: map[string]bool{\n", path) + pkg := pkgs[path] + var syms []string + for sym := range pkg { + syms = append(syms, sym) + } + sort.Strings(syms) + for _, sym := range syms { + outf("\t\t%q: true,\n", sym) + } + outf("},\n") + } + outf("}\n") + fmtbuf, err := format.Source(buf.Bytes()) + if err != nil { + log.Fatal(err) + } + err = ioutil.WriteFile("zstdlib.go", fmtbuf, 0666) + if err != nil { + log.Fatal(err) + } +} + +// syscallJSAPI returns the API of the syscall/js package. +// It's computed from the contents of $(go env GOROOT)/src/syscall/js. +func syscallJSAPI() io.Reader { + var exeSuffix string + if runtime.GOOS == "windows" { + exeSuffix = ".exe" + } + cmd := exec.Command("go"+exeSuffix, "run", "cmd/api", "-contexts", "js-wasm", "syscall/js") + out, err := cmd.Output() + if err != nil { + log.Fatalln(err) + } + return bytes.NewReader(out) +} diff --git a/vendor/golang.org/x/tools/imports/mod.go b/vendor/golang.org/x/tools/imports/mod.go new file mode 100644 index 0000000000..018c43ce84 --- /dev/null +++ b/vendor/golang.org/x/tools/imports/mod.go @@ -0,0 +1,355 @@ +package imports + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "time" + + "golang.org/x/tools/internal/gopathwalk" + "golang.org/x/tools/internal/module" +) + +// moduleResolver implements resolver for modules using the go command as little +// as feasible. +type moduleResolver struct { + env *fixEnv + + initialized bool + main *moduleJSON + modsByModPath []*moduleJSON // All modules, ordered by # of path components in module Path... + modsByDir []*moduleJSON // ...or Dir. +} + +type moduleJSON struct { + Path string // module path + Version string // module version + Versions []string // available module versions (with -versions) + Replace *moduleJSON // replaced by this module + Time *time.Time // time version was created + Update *moduleJSON // available update, if any (with -u) + Main bool // is this the main module? + Indirect bool // is this module only an indirect dependency of main module? + Dir string // directory holding files for this module, if any + GoMod string // path to go.mod file for this module, if any + Error *moduleErrorJSON // error loading module +} + +type moduleErrorJSON struct { + Err string // the error itself +} + +func (r *moduleResolver) init() error { + if r.initialized { + return nil + } + stdout, err := r.env.invokeGo("list", "-m", "-json", "...") + if err != nil { + return err + } + for dec := json.NewDecoder(stdout); dec.More(); { + mod := &moduleJSON{} + if err := dec.Decode(mod); err != nil { + return err + } + if mod.Dir == "" { + if Debug { + log.Printf("module %v has not been downloaded and will be ignored", mod.Path) + } + // Can't do anything with a module that's not downloaded. + continue + } + r.modsByModPath = append(r.modsByModPath, mod) + r.modsByDir = append(r.modsByDir, mod) + if mod.Main { + r.main = mod + } + } + + sort.Slice(r.modsByModPath, func(i, j int) bool { + count := func(x int) int { + return strings.Count(r.modsByModPath[x].Path, "/") + } + return count(j) < count(i) // descending order + }) + sort.Slice(r.modsByDir, func(i, j int) bool { + count := func(x int) int { + return strings.Count(r.modsByDir[x].Dir, "/") + } + return count(j) < count(i) // descending order + }) + + r.initialized = true + return nil +} + +// findPackage returns the module and directory that contains the package at +// the given import path, or returns nil, "" if no module is in scope. +func (r *moduleResolver) findPackage(importPath string) (*moduleJSON, string) { + for _, m := range r.modsByModPath { + if !strings.HasPrefix(importPath, m.Path) { + continue + } + pathInModule := importPath[len(m.Path):] + pkgDir := filepath.Join(m.Dir, pathInModule) + if dirIsNestedModule(pkgDir, m) { + continue + } + + pkgFiles, err := ioutil.ReadDir(pkgDir) + if err != nil { + continue + } + + // A module only contains a package if it has buildable go + // files in that directory. If not, it could be provided by an + // outer module. See #29736. + for _, fi := range pkgFiles { + if ok, _ := r.env.buildContext().MatchFile(pkgDir, fi.Name()); ok { + return m, pkgDir + } + } + } + return nil, "" +} + +// findModuleByDir returns the module that contains dir, or nil if no such +// module is in scope. +func (r *moduleResolver) findModuleByDir(dir string) *moduleJSON { + // This is quite tricky and may not be correct. dir could be: + // - a package in the main module. + // - a replace target underneath the main module's directory. + // - a nested module in the above. + // - a replace target somewhere totally random. + // - a nested module in the above. + // - in the mod cache. + // - in /vendor/ in -mod=vendor mode. + // - nested module? Dunno. + // Rumor has it that replace targets cannot contain other replace targets. + for _, m := range r.modsByDir { + if !strings.HasPrefix(dir, m.Dir) { + continue + } + + if dirIsNestedModule(dir, m) { + continue + } + + return m + } + return nil +} + +// dirIsNestedModule reports if dir is contained in a nested module underneath +// mod, not actually in mod. +func dirIsNestedModule(dir string, mod *moduleJSON) bool { + if !strings.HasPrefix(dir, mod.Dir) { + return false + } + mf := findModFile(dir) + if mf == "" { + return false + } + return filepath.Dir(mf) != mod.Dir +} + +func findModFile(dir string) string { + for { + f := filepath.Join(dir, "go.mod") + info, err := os.Stat(f) + if err == nil && !info.IsDir() { + return f + } + d := filepath.Dir(dir) + if len(d) >= len(dir) { + return "" // reached top of file system, no go.mod + } + dir = d + } +} + +func (r *moduleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { + if err := r.init(); err != nil { + return nil, err + } + names := map[string]string{} + for _, path := range importPaths { + _, packageDir := r.findPackage(path) + if packageDir == "" { + continue + } + name, err := packageDirToName(packageDir) + if err != nil { + continue + } + names[path] = name + } + return names, nil +} + +func (r *moduleResolver) scan(_ references) ([]*pkg, error) { + if err := r.init(); err != nil { + return nil, err + } + + // Walk GOROOT, GOPATH/pkg/mod, and the main module. + roots := []gopathwalk.Root{ + {filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT}, + } + if r.main != nil { + roots = append(roots, gopathwalk.Root{r.main.Dir, gopathwalk.RootCurrentModule}) + } + for _, p := range filepath.SplitList(r.env.GOPATH) { + roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache}) + } + + // Walk replace targets, just in case they're not in any of the above. + for _, mod := range r.modsByModPath { + if mod.Replace != nil { + roots = append(roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther}) + } + } + + var result []*pkg + dupCheck := make(map[string]bool) + var mu sync.Mutex + + gopathwalk.Walk(roots, func(root gopathwalk.Root, dir string) { + mu.Lock() + defer mu.Unlock() + + if _, dup := dupCheck[dir]; dup { + return + } + + dupCheck[dir] = true + + subdir := "" + if dir != root.Path { + subdir = dir[len(root.Path)+len("/"):] + } + importPath := filepath.ToSlash(subdir) + if strings.HasPrefix(importPath, "vendor/") { + // Ignore vendor dirs. If -mod=vendor is on, then things + // should mostly just work, but when it's not vendor/ + // is a mess. There's no easy way to tell if it's on. + // We can still find things in the mod cache and + // map them into /vendor when -mod=vendor is on. + return + } + switch root.Type { + case gopathwalk.RootCurrentModule: + importPath = path.Join(r.main.Path, filepath.ToSlash(subdir)) + case gopathwalk.RootModuleCache: + matches := modCacheRegexp.FindStringSubmatch(subdir) + modPath, err := module.DecodePath(filepath.ToSlash(matches[1])) + if err != nil { + if Debug { + log.Printf("decoding module cache path %q: %v", subdir, err) + } + return + } + importPath = path.Join(modPath, filepath.ToSlash(matches[3])) + case gopathwalk.RootGOROOT: + importPath = subdir + } + + // Check if the directory is underneath a module that's in scope. + if mod := r.findModuleByDir(dir); mod != nil { + // It is. If dir is the target of a replace directive, + // our guessed import path is wrong. Use the real one. + if mod.Dir == dir { + importPath = mod.Path + } else { + dirInMod := dir[len(mod.Dir)+len("/"):] + importPath = path.Join(mod.Path, filepath.ToSlash(dirInMod)) + } + } else { + // The package is in an unknown module. Check that it's + // not obviously impossible to import. + var modFile string + switch root.Type { + case gopathwalk.RootModuleCache: + matches := modCacheRegexp.FindStringSubmatch(subdir) + modFile = filepath.Join(matches[1], "@", matches[2], "go.mod") + default: + modFile = findModFile(dir) + } + + modBytes, err := ioutil.ReadFile(modFile) + if err == nil && !strings.HasPrefix(importPath, modulePath(modBytes)) { + // The module's declared path does not match + // its expected path. It probably needs a + // replace directive we don't have. + return + } + } + // We may have discovered a package that has a different version + // in scope already. Canonicalize to that one if possible. + if _, canonicalDir := r.findPackage(importPath); canonicalDir != "" { + dir = canonicalDir + } + + result = append(result, &pkg{ + importPathShort: VendorlessPath(importPath), + dir: dir, + }) + }, gopathwalk.Options{Debug: Debug, ModulesEnabled: true}) + return result, nil +} + +// modCacheRegexp splits a path in a module cache into module, module version, and package. +var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) + +var ( + slashSlash = []byte("//") + moduleStr = []byte("module") +) + +// modulePath returns the module path from the gomod file text. +// If it cannot find a module path, it returns an empty string. +// It is tolerant of unrelated problems in the go.mod file. +// +// Copied from cmd/go/internal/modfile. +func modulePath(mod []byte) string { + for len(mod) > 0 { + line := mod + mod = nil + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, mod = line[:i], line[i+1:] + } + if i := bytes.Index(line, slashSlash); i >= 0 { + line = line[:i] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, moduleStr) { + continue + } + line = line[len(moduleStr):] + n := len(line) + line = bytes.TrimSpace(line) + if len(line) == n || len(line) == 0 { + continue + } + + if line[0] == '"' || line[0] == '`' { + p, err := strconv.Unquote(string(line)) + if err != nil { + return "" // malformed quoted string or multiline module path + } + return p + } + + return string(line) + } + return "" // missing module path +} diff --git a/vendor/golang.org/x/tools/imports/sortimports.go b/vendor/golang.org/x/tools/imports/sortimports.go new file mode 100644 index 0000000000..f3dd56c7a6 --- /dev/null +++ b/vendor/golang.org/x/tools/imports/sortimports.go @@ -0,0 +1,230 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Hacked up copy of go/ast/import.go + +package imports + +import ( + "go/ast" + "go/token" + "sort" + "strconv" +) + +// sortImports sorts runs of consecutive import lines in import blocks in f. +// It also removes duplicate imports when it is possible to do so without data loss. +func sortImports(fset *token.FileSet, f *ast.File) { + for i, d := range f.Decls { + d, ok := d.(*ast.GenDecl) + if !ok || d.Tok != token.IMPORT { + // Not an import declaration, so we're done. + // Imports are always first. + break + } + + if len(d.Specs) == 0 { + // Empty import block, remove it. + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + } + + if !d.Lparen.IsValid() { + // Not a block: sorted by default. + continue + } + + // Identify and sort runs of specs on successive lines. + i := 0 + specs := d.Specs[:0] + for j, s := range d.Specs { + if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line { + // j begins a new run. End this one. + specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...) + i = j + } + } + specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...) + d.Specs = specs + + // Deduping can leave a blank line before the rparen; clean that up. + if len(d.Specs) > 0 { + lastSpec := d.Specs[len(d.Specs)-1] + lastLine := fset.Position(lastSpec.Pos()).Line + if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 { + fset.File(d.Rparen).MergeLine(rParenLine - 1) + } + } + } +} + +func importPath(s ast.Spec) string { + t, err := strconv.Unquote(s.(*ast.ImportSpec).Path.Value) + if err == nil { + return t + } + return "" +} + +func importName(s ast.Spec) string { + n := s.(*ast.ImportSpec).Name + if n == nil { + return "" + } + return n.Name +} + +func importComment(s ast.Spec) string { + c := s.(*ast.ImportSpec).Comment + if c == nil { + return "" + } + return c.Text() +} + +// collapse indicates whether prev may be removed, leaving only next. +func collapse(prev, next ast.Spec) bool { + if importPath(next) != importPath(prev) || importName(next) != importName(prev) { + return false + } + return prev.(*ast.ImportSpec).Comment == nil +} + +type posSpan struct { + Start token.Pos + End token.Pos +} + +func sortSpecs(fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec { + // Can't short-circuit here even if specs are already sorted, + // since they might yet need deduplication. + // A lone import, however, may be safely ignored. + if len(specs) <= 1 { + return specs + } + + // Record positions for specs. + pos := make([]posSpan, len(specs)) + for i, s := range specs { + pos[i] = posSpan{s.Pos(), s.End()} + } + + // Identify comments in this range. + // Any comment from pos[0].Start to the final line counts. + lastLine := fset.Position(pos[len(pos)-1].End).Line + cstart := len(f.Comments) + cend := len(f.Comments) + for i, g := range f.Comments { + if g.Pos() < pos[0].Start { + continue + } + if i < cstart { + cstart = i + } + if fset.Position(g.End()).Line > lastLine { + cend = i + break + } + } + comments := f.Comments[cstart:cend] + + // Assign each comment to the import spec preceding it. + importComment := map[*ast.ImportSpec][]*ast.CommentGroup{} + specIndex := 0 + for _, g := range comments { + for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() { + specIndex++ + } + s := specs[specIndex].(*ast.ImportSpec) + importComment[s] = append(importComment[s], g) + } + + // Sort the import specs by import path. + // Remove duplicates, when possible without data loss. + // Reassign the import paths to have the same position sequence. + // Reassign each comment to abut the end of its spec. + // Sort the comments by new position. + sort.Sort(byImportSpec(specs)) + + // Dedup. Thanks to our sorting, we can just consider + // adjacent pairs of imports. + deduped := specs[:0] + for i, s := range specs { + if i == len(specs)-1 || !collapse(s, specs[i+1]) { + deduped = append(deduped, s) + } else { + p := s.Pos() + fset.File(p).MergeLine(fset.Position(p).Line) + } + } + specs = deduped + + // Fix up comment positions + for i, s := range specs { + s := s.(*ast.ImportSpec) + if s.Name != nil { + s.Name.NamePos = pos[i].Start + } + s.Path.ValuePos = pos[i].Start + s.EndPos = pos[i].End + nextSpecPos := pos[i].End + + for _, g := range importComment[s] { + for _, c := range g.List { + c.Slash = pos[i].End + nextSpecPos = c.End() + } + } + if i < len(specs)-1 { + pos[i+1].Start = nextSpecPos + pos[i+1].End = nextSpecPos + } + } + + sort.Sort(byCommentPos(comments)) + + // Fixup comments can insert blank lines, because import specs are on different lines. + // We remove those blank lines here by merging import spec to the first import spec line. + firstSpecLine := fset.Position(specs[0].Pos()).Line + for _, s := range specs[1:] { + p := s.Pos() + line := fset.File(p).Line(p) + for previousLine := line - 1; previousLine >= firstSpecLine; { + fset.File(p).MergeLine(previousLine) + previousLine-- + } + } + return specs +} + +type byImportSpec []ast.Spec // slice of *ast.ImportSpec + +func (x byImportSpec) Len() int { return len(x) } +func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byImportSpec) Less(i, j int) bool { + ipath := importPath(x[i]) + jpath := importPath(x[j]) + + igroup := importGroup(ipath) + jgroup := importGroup(jpath) + if igroup != jgroup { + return igroup < jgroup + } + + if ipath != jpath { + return ipath < jpath + } + iname := importName(x[i]) + jname := importName(x[j]) + + if iname != jname { + return iname < jname + } + return importComment(x[i]) < importComment(x[j]) +} + +type byCommentPos []*ast.CommentGroup + +func (x byCommentPos) Len() int { return len(x) } +func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() } diff --git a/vendor/golang.org/x/tools/imports/zstdlib.go b/vendor/golang.org/x/tools/imports/zstdlib.go new file mode 100644 index 0000000000..d81b8c5307 --- /dev/null +++ b/vendor/golang.org/x/tools/imports/zstdlib.go @@ -0,0 +1,10325 @@ +// Code generated by mkstdlib.go. DO NOT EDIT. + +package imports + +var stdlib = map[string]map[string]bool{ + "archive/tar": map[string]bool{ + "ErrFieldTooLong": true, + "ErrHeader": true, + "ErrWriteAfterClose": true, + "ErrWriteTooLong": true, + "FileInfoHeader": true, + "Format": true, + "FormatGNU": true, + "FormatPAX": true, + "FormatUSTAR": true, + "FormatUnknown": true, + "Header": true, + "NewReader": true, + "NewWriter": true, + "Reader": true, + "TypeBlock": true, + "TypeChar": true, + "TypeCont": true, + "TypeDir": true, + "TypeFifo": true, + "TypeGNULongLink": true, + "TypeGNULongName": true, + "TypeGNUSparse": true, + "TypeLink": true, + "TypeReg": true, + "TypeRegA": true, + "TypeSymlink": true, + "TypeXGlobalHeader": true, + "TypeXHeader": true, + "Writer": true, + }, + "archive/zip": map[string]bool{ + "Compressor": true, + "Decompressor": true, + "Deflate": true, + "ErrAlgorithm": true, + "ErrChecksum": true, + "ErrFormat": true, + "File": true, + "FileHeader": true, + "FileInfoHeader": true, + "NewReader": true, + "NewWriter": true, + "OpenReader": true, + "ReadCloser": true, + "Reader": true, + "RegisterCompressor": true, + "RegisterDecompressor": true, + "Store": true, + "Writer": true, + }, + "bufio": map[string]bool{ + "ErrAdvanceTooFar": true, + "ErrBufferFull": true, + "ErrFinalToken": true, + "ErrInvalidUnreadByte": true, + "ErrInvalidUnreadRune": true, + "ErrNegativeAdvance": true, + "ErrNegativeCount": true, + "ErrTooLong": true, + "MaxScanTokenSize": true, + "NewReadWriter": true, + "NewReader": true, + "NewReaderSize": true, + "NewScanner": true, + "NewWriter": true, + "NewWriterSize": true, + "ReadWriter": true, + "Reader": true, + "ScanBytes": true, + "ScanLines": true, + "ScanRunes": true, + "ScanWords": true, + "Scanner": true, + "SplitFunc": true, + "Writer": true, + }, + "bytes": map[string]bool{ + "Buffer": true, + "Compare": true, + "Contains": true, + "ContainsAny": true, + "ContainsRune": true, + "Count": true, + "Equal": true, + "EqualFold": true, + "ErrTooLarge": true, + "Fields": true, + "FieldsFunc": true, + "HasPrefix": true, + "HasSuffix": true, + "Index": true, + "IndexAny": true, + "IndexByte": true, + "IndexFunc": true, + "IndexRune": true, + "Join": true, + "LastIndex": true, + "LastIndexAny": true, + "LastIndexByte": true, + "LastIndexFunc": true, + "Map": true, + "MinRead": true, + "NewBuffer": true, + "NewBufferString": true, + "NewReader": true, + "Reader": true, + "Repeat": true, + "Replace": true, + "ReplaceAll": true, + "Runes": true, + "Split": true, + "SplitAfter": true, + "SplitAfterN": true, + "SplitN": true, + "Title": true, + "ToLower": true, + "ToLowerSpecial": true, + "ToTitle": true, + "ToTitleSpecial": true, + "ToUpper": true, + "ToUpperSpecial": true, + "Trim": true, + "TrimFunc": true, + "TrimLeft": true, + "TrimLeftFunc": true, + "TrimPrefix": true, + "TrimRight": true, + "TrimRightFunc": true, + "TrimSpace": true, + "TrimSuffix": true, + }, + "compress/bzip2": map[string]bool{ + "NewReader": true, + "StructuralError": true, + }, + "compress/flate": map[string]bool{ + "BestCompression": true, + "BestSpeed": true, + "CorruptInputError": true, + "DefaultCompression": true, + "HuffmanOnly": true, + "InternalError": true, + "NewReader": true, + "NewReaderDict": true, + "NewWriter": true, + "NewWriterDict": true, + "NoCompression": true, + "ReadError": true, + "Reader": true, + "Resetter": true, + "WriteError": true, + "Writer": true, + }, + "compress/gzip": map[string]bool{ + "BestCompression": true, + "BestSpeed": true, + "DefaultCompression": true, + "ErrChecksum": true, + "ErrHeader": true, + "Header": true, + "HuffmanOnly": true, + "NewReader": true, + "NewWriter": true, + "NewWriterLevel": true, + "NoCompression": true, + "Reader": true, + "Writer": true, + }, + "compress/lzw": map[string]bool{ + "LSB": true, + "MSB": true, + "NewReader": true, + "NewWriter": true, + "Order": true, + }, + "compress/zlib": map[string]bool{ + "BestCompression": true, + "BestSpeed": true, + "DefaultCompression": true, + "ErrChecksum": true, + "ErrDictionary": true, + "ErrHeader": true, + "HuffmanOnly": true, + "NewReader": true, + "NewReaderDict": true, + "NewWriter": true, + "NewWriterLevel": true, + "NewWriterLevelDict": true, + "NoCompression": true, + "Resetter": true, + "Writer": true, + }, + "container/heap": map[string]bool{ + "Fix": true, + "Init": true, + "Interface": true, + "Pop": true, + "Push": true, + "Remove": true, + }, + "container/list": map[string]bool{ + "Element": true, + "List": true, + "New": true, + }, + "container/ring": map[string]bool{ + "New": true, + "Ring": true, + }, + "context": map[string]bool{ + "Background": true, + "CancelFunc": true, + "Canceled": true, + "Context": true, + "DeadlineExceeded": true, + "TODO": true, + "WithCancel": true, + "WithDeadline": true, + "WithTimeout": true, + "WithValue": true, + }, + "crypto": map[string]bool{ + "BLAKE2b_256": true, + "BLAKE2b_384": true, + "BLAKE2b_512": true, + "BLAKE2s_256": true, + "Decrypter": true, + "DecrypterOpts": true, + "Hash": true, + "MD4": true, + "MD5": true, + "MD5SHA1": true, + "PrivateKey": true, + "PublicKey": true, + "RIPEMD160": true, + "RegisterHash": true, + "SHA1": true, + "SHA224": true, + "SHA256": true, + "SHA384": true, + "SHA3_224": true, + "SHA3_256": true, + "SHA3_384": true, + "SHA3_512": true, + "SHA512": true, + "SHA512_224": true, + "SHA512_256": true, + "Signer": true, + "SignerOpts": true, + }, + "crypto/aes": map[string]bool{ + "BlockSize": true, + "KeySizeError": true, + "NewCipher": true, + }, + "crypto/cipher": map[string]bool{ + "AEAD": true, + "Block": true, + "BlockMode": true, + "NewCBCDecrypter": true, + "NewCBCEncrypter": true, + "NewCFBDecrypter": true, + "NewCFBEncrypter": true, + "NewCTR": true, + "NewGCM": true, + "NewGCMWithNonceSize": true, + "NewGCMWithTagSize": true, + "NewOFB": true, + "Stream": true, + "StreamReader": true, + "StreamWriter": true, + }, + "crypto/des": map[string]bool{ + "BlockSize": true, + "KeySizeError": true, + "NewCipher": true, + "NewTripleDESCipher": true, + }, + "crypto/dsa": map[string]bool{ + "ErrInvalidPublicKey": true, + "GenerateKey": true, + "GenerateParameters": true, + "L1024N160": true, + "L2048N224": true, + "L2048N256": true, + "L3072N256": true, + "ParameterSizes": true, + "Parameters": true, + "PrivateKey": true, + "PublicKey": true, + "Sign": true, + "Verify": true, + }, + "crypto/ecdsa": map[string]bool{ + "GenerateKey": true, + "PrivateKey": true, + "PublicKey": true, + "Sign": true, + "Verify": true, + }, + "crypto/elliptic": map[string]bool{ + "Curve": true, + "CurveParams": true, + "GenerateKey": true, + "Marshal": true, + "P224": true, + "P256": true, + "P384": true, + "P521": true, + "Unmarshal": true, + }, + "crypto/hmac": map[string]bool{ + "Equal": true, + "New": true, + }, + "crypto/md5": map[string]bool{ + "BlockSize": true, + "New": true, + "Size": true, + "Sum": true, + }, + "crypto/rand": map[string]bool{ + "Int": true, + "Prime": true, + "Read": true, + "Reader": true, + }, + "crypto/rc4": map[string]bool{ + "Cipher": true, + "KeySizeError": true, + "NewCipher": true, + }, + "crypto/rsa": map[string]bool{ + "CRTValue": true, + "DecryptOAEP": true, + "DecryptPKCS1v15": true, + "DecryptPKCS1v15SessionKey": true, + "EncryptOAEP": true, + "EncryptPKCS1v15": true, + "ErrDecryption": true, + "ErrMessageTooLong": true, + "ErrVerification": true, + "GenerateKey": true, + "GenerateMultiPrimeKey": true, + "OAEPOptions": true, + "PKCS1v15DecryptOptions": true, + "PSSOptions": true, + "PSSSaltLengthAuto": true, + "PSSSaltLengthEqualsHash": true, + "PrecomputedValues": true, + "PrivateKey": true, + "PublicKey": true, + "SignPKCS1v15": true, + "SignPSS": true, + "VerifyPKCS1v15": true, + "VerifyPSS": true, + }, + "crypto/sha1": map[string]bool{ + "BlockSize": true, + "New": true, + "Size": true, + "Sum": true, + }, + "crypto/sha256": map[string]bool{ + "BlockSize": true, + "New": true, + "New224": true, + "Size": true, + "Size224": true, + "Sum224": true, + "Sum256": true, + }, + "crypto/sha512": map[string]bool{ + "BlockSize": true, + "New": true, + "New384": true, + "New512_224": true, + "New512_256": true, + "Size": true, + "Size224": true, + "Size256": true, + "Size384": true, + "Sum384": true, + "Sum512": true, + "Sum512_224": true, + "Sum512_256": true, + }, + "crypto/subtle": map[string]bool{ + "ConstantTimeByteEq": true, + "ConstantTimeCompare": true, + "ConstantTimeCopy": true, + "ConstantTimeEq": true, + "ConstantTimeLessOrEq": true, + "ConstantTimeSelect": true, + }, + "crypto/tls": map[string]bool{ + "Certificate": true, + "CertificateRequestInfo": true, + "Client": true, + "ClientAuthType": true, + "ClientHelloInfo": true, + "ClientSessionCache": true, + "ClientSessionState": true, + "Config": true, + "Conn": true, + "ConnectionState": true, + "CurveID": true, + "CurveP256": true, + "CurveP384": true, + "CurveP521": true, + "Dial": true, + "DialWithDialer": true, + "ECDSAWithP256AndSHA256": true, + "ECDSAWithP384AndSHA384": true, + "ECDSAWithP521AndSHA512": true, + "ECDSAWithSHA1": true, + "Listen": true, + "LoadX509KeyPair": true, + "NewLRUClientSessionCache": true, + "NewListener": true, + "NoClientCert": true, + "PKCS1WithSHA1": true, + "PKCS1WithSHA256": true, + "PKCS1WithSHA384": true, + "PKCS1WithSHA512": true, + "PSSWithSHA256": true, + "PSSWithSHA384": true, + "PSSWithSHA512": true, + "RecordHeaderError": true, + "RenegotiateFreelyAsClient": true, + "RenegotiateNever": true, + "RenegotiateOnceAsClient": true, + "RenegotiationSupport": true, + "RequestClientCert": true, + "RequireAndVerifyClientCert": true, + "RequireAnyClientCert": true, + "Server": true, + "SignatureScheme": true, + "TLS_AES_128_GCM_SHA256": true, + "TLS_AES_256_GCM_SHA384": true, + "TLS_CHACHA20_POLY1305_SHA256": true, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": true, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": true, + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": true, + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": true, + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": true, + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": true, + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": true, + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": true, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": true, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": true, + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": true, + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": true, + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": true, + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": true, + "TLS_ECDHE_RSA_WITH_RC4_128_SHA": true, + "TLS_FALLBACK_SCSV": true, + "TLS_RSA_WITH_3DES_EDE_CBC_SHA": true, + "TLS_RSA_WITH_AES_128_CBC_SHA": true, + "TLS_RSA_WITH_AES_128_CBC_SHA256": true, + "TLS_RSA_WITH_AES_128_GCM_SHA256": true, + "TLS_RSA_WITH_AES_256_CBC_SHA": true, + "TLS_RSA_WITH_AES_256_GCM_SHA384": true, + "TLS_RSA_WITH_RC4_128_SHA": true, + "VerifyClientCertIfGiven": true, + "VersionSSL30": true, + "VersionTLS10": true, + "VersionTLS11": true, + "VersionTLS12": true, + "VersionTLS13": true, + "X25519": true, + "X509KeyPair": true, + }, + "crypto/x509": map[string]bool{ + "CANotAuthorizedForExtKeyUsage": true, + "CANotAuthorizedForThisName": true, + "CertPool": true, + "Certificate": true, + "CertificateInvalidError": true, + "CertificateRequest": true, + "ConstraintViolationError": true, + "CreateCertificate": true, + "CreateCertificateRequest": true, + "DSA": true, + "DSAWithSHA1": true, + "DSAWithSHA256": true, + "DecryptPEMBlock": true, + "ECDSA": true, + "ECDSAWithSHA1": true, + "ECDSAWithSHA256": true, + "ECDSAWithSHA384": true, + "ECDSAWithSHA512": true, + "EncryptPEMBlock": true, + "ErrUnsupportedAlgorithm": true, + "Expired": true, + "ExtKeyUsage": true, + "ExtKeyUsageAny": true, + "ExtKeyUsageClientAuth": true, + "ExtKeyUsageCodeSigning": true, + "ExtKeyUsageEmailProtection": true, + "ExtKeyUsageIPSECEndSystem": true, + "ExtKeyUsageIPSECTunnel": true, + "ExtKeyUsageIPSECUser": true, + "ExtKeyUsageMicrosoftCommercialCodeSigning": true, + "ExtKeyUsageMicrosoftKernelCodeSigning": true, + "ExtKeyUsageMicrosoftServerGatedCrypto": true, + "ExtKeyUsageNetscapeServerGatedCrypto": true, + "ExtKeyUsageOCSPSigning": true, + "ExtKeyUsageServerAuth": true, + "ExtKeyUsageTimeStamping": true, + "HostnameError": true, + "IncompatibleUsage": true, + "IncorrectPasswordError": true, + "InsecureAlgorithmError": true, + "InvalidReason": true, + "IsEncryptedPEMBlock": true, + "KeyUsage": true, + "KeyUsageCRLSign": true, + "KeyUsageCertSign": true, + "KeyUsageContentCommitment": true, + "KeyUsageDataEncipherment": true, + "KeyUsageDecipherOnly": true, + "KeyUsageDigitalSignature": true, + "KeyUsageEncipherOnly": true, + "KeyUsageKeyAgreement": true, + "KeyUsageKeyEncipherment": true, + "MD2WithRSA": true, + "MD5WithRSA": true, + "MarshalECPrivateKey": true, + "MarshalPKCS1PrivateKey": true, + "MarshalPKCS1PublicKey": true, + "MarshalPKCS8PrivateKey": true, + "MarshalPKIXPublicKey": true, + "NameConstraintsWithoutSANs": true, + "NameMismatch": true, + "NewCertPool": true, + "NotAuthorizedToSign": true, + "PEMCipher": true, + "PEMCipher3DES": true, + "PEMCipherAES128": true, + "PEMCipherAES192": true, + "PEMCipherAES256": true, + "PEMCipherDES": true, + "ParseCRL": true, + "ParseCertificate": true, + "ParseCertificateRequest": true, + "ParseCertificates": true, + "ParseDERCRL": true, + "ParseECPrivateKey": true, + "ParsePKCS1PrivateKey": true, + "ParsePKCS1PublicKey": true, + "ParsePKCS8PrivateKey": true, + "ParsePKIXPublicKey": true, + "PublicKeyAlgorithm": true, + "RSA": true, + "SHA1WithRSA": true, + "SHA256WithRSA": true, + "SHA256WithRSAPSS": true, + "SHA384WithRSA": true, + "SHA384WithRSAPSS": true, + "SHA512WithRSA": true, + "SHA512WithRSAPSS": true, + "SignatureAlgorithm": true, + "SystemCertPool": true, + "SystemRootsError": true, + "TooManyConstraints": true, + "TooManyIntermediates": true, + "UnconstrainedName": true, + "UnhandledCriticalExtension": true, + "UnknownAuthorityError": true, + "UnknownPublicKeyAlgorithm": true, + "UnknownSignatureAlgorithm": true, + "VerifyOptions": true, + }, + "crypto/x509/pkix": map[string]bool{ + "AlgorithmIdentifier": true, + "AttributeTypeAndValue": true, + "AttributeTypeAndValueSET": true, + "CertificateList": true, + "Extension": true, + "Name": true, + "RDNSequence": true, + "RelativeDistinguishedNameSET": true, + "RevokedCertificate": true, + "TBSCertificateList": true, + }, + "database/sql": map[string]bool{ + "ColumnType": true, + "Conn": true, + "DB": true, + "DBStats": true, + "Drivers": true, + "ErrConnDone": true, + "ErrNoRows": true, + "ErrTxDone": true, + "IsolationLevel": true, + "LevelDefault": true, + "LevelLinearizable": true, + "LevelReadCommitted": true, + "LevelReadUncommitted": true, + "LevelRepeatableRead": true, + "LevelSerializable": true, + "LevelSnapshot": true, + "LevelWriteCommitted": true, + "Named": true, + "NamedArg": true, + "NullBool": true, + "NullFloat64": true, + "NullInt64": true, + "NullString": true, + "Open": true, + "OpenDB": true, + "Out": true, + "RawBytes": true, + "Register": true, + "Result": true, + "Row": true, + "Rows": true, + "Scanner": true, + "Stmt": true, + "Tx": true, + "TxOptions": true, + }, + "database/sql/driver": map[string]bool{ + "Bool": true, + "ColumnConverter": true, + "Conn": true, + "ConnBeginTx": true, + "ConnPrepareContext": true, + "Connector": true, + "DefaultParameterConverter": true, + "Driver": true, + "DriverContext": true, + "ErrBadConn": true, + "ErrRemoveArgument": true, + "ErrSkip": true, + "Execer": true, + "ExecerContext": true, + "Int32": true, + "IsScanValue": true, + "IsValue": true, + "IsolationLevel": true, + "NamedValue": true, + "NamedValueChecker": true, + "NotNull": true, + "Null": true, + "Pinger": true, + "Queryer": true, + "QueryerContext": true, + "Result": true, + "ResultNoRows": true, + "Rows": true, + "RowsAffected": true, + "RowsColumnTypeDatabaseTypeName": true, + "RowsColumnTypeLength": true, + "RowsColumnTypeNullable": true, + "RowsColumnTypePrecisionScale": true, + "RowsColumnTypeScanType": true, + "RowsNextResultSet": true, + "SessionResetter": true, + "Stmt": true, + "StmtExecContext": true, + "StmtQueryContext": true, + "String": true, + "Tx": true, + "TxOptions": true, + "Value": true, + "ValueConverter": true, + "Valuer": true, + }, + "debug/dwarf": map[string]bool{ + "AddrType": true, + "ArrayType": true, + "Attr": true, + "AttrAbstractOrigin": true, + "AttrAccessibility": true, + "AttrAddrClass": true, + "AttrAllocated": true, + "AttrArtificial": true, + "AttrAssociated": true, + "AttrBaseTypes": true, + "AttrBitOffset": true, + "AttrBitSize": true, + "AttrByteSize": true, + "AttrCallColumn": true, + "AttrCallFile": true, + "AttrCallLine": true, + "AttrCalling": true, + "AttrCommonRef": true, + "AttrCompDir": true, + "AttrConstValue": true, + "AttrContainingType": true, + "AttrCount": true, + "AttrDataLocation": true, + "AttrDataMemberLoc": true, + "AttrDeclColumn": true, + "AttrDeclFile": true, + "AttrDeclLine": true, + "AttrDeclaration": true, + "AttrDefaultValue": true, + "AttrDescription": true, + "AttrDiscr": true, + "AttrDiscrList": true, + "AttrDiscrValue": true, + "AttrEncoding": true, + "AttrEntrypc": true, + "AttrExtension": true, + "AttrExternal": true, + "AttrFrameBase": true, + "AttrFriend": true, + "AttrHighpc": true, + "AttrIdentifierCase": true, + "AttrImport": true, + "AttrInline": true, + "AttrIsOptional": true, + "AttrLanguage": true, + "AttrLocation": true, + "AttrLowerBound": true, + "AttrLowpc": true, + "AttrMacroInfo": true, + "AttrName": true, + "AttrNamelistItem": true, + "AttrOrdering": true, + "AttrPriority": true, + "AttrProducer": true, + "AttrPrototyped": true, + "AttrRanges": true, + "AttrReturnAddr": true, + "AttrSegment": true, + "AttrSibling": true, + "AttrSpecification": true, + "AttrStartScope": true, + "AttrStaticLink": true, + "AttrStmtList": true, + "AttrStride": true, + "AttrStrideSize": true, + "AttrStringLength": true, + "AttrTrampoline": true, + "AttrType": true, + "AttrUpperBound": true, + "AttrUseLocation": true, + "AttrUseUTF8": true, + "AttrVarParam": true, + "AttrVirtuality": true, + "AttrVisibility": true, + "AttrVtableElemLoc": true, + "BasicType": true, + "BoolType": true, + "CharType": true, + "Class": true, + "ClassAddress": true, + "ClassBlock": true, + "ClassConstant": true, + "ClassExprLoc": true, + "ClassFlag": true, + "ClassLinePtr": true, + "ClassLocListPtr": true, + "ClassMacPtr": true, + "ClassRangeListPtr": true, + "ClassReference": true, + "ClassReferenceAlt": true, + "ClassReferenceSig": true, + "ClassString": true, + "ClassStringAlt": true, + "ClassUnknown": true, + "CommonType": true, + "ComplexType": true, + "Data": true, + "DecodeError": true, + "DotDotDotType": true, + "Entry": true, + "EnumType": true, + "EnumValue": true, + "ErrUnknownPC": true, + "Field": true, + "FloatType": true, + "FuncType": true, + "IntType": true, + "LineEntry": true, + "LineFile": true, + "LineReader": true, + "LineReaderPos": true, + "New": true, + "Offset": true, + "PtrType": true, + "QualType": true, + "Reader": true, + "StructField": true, + "StructType": true, + "Tag": true, + "TagAccessDeclaration": true, + "TagArrayType": true, + "TagBaseType": true, + "TagCatchDwarfBlock": true, + "TagClassType": true, + "TagCommonDwarfBlock": true, + "TagCommonInclusion": true, + "TagCompileUnit": true, + "TagCondition": true, + "TagConstType": true, + "TagConstant": true, + "TagDwarfProcedure": true, + "TagEntryPoint": true, + "TagEnumerationType": true, + "TagEnumerator": true, + "TagFileType": true, + "TagFormalParameter": true, + "TagFriend": true, + "TagImportedDeclaration": true, + "TagImportedModule": true, + "TagImportedUnit": true, + "TagInheritance": true, + "TagInlinedSubroutine": true, + "TagInterfaceType": true, + "TagLabel": true, + "TagLexDwarfBlock": true, + "TagMember": true, + "TagModule": true, + "TagMutableType": true, + "TagNamelist": true, + "TagNamelistItem": true, + "TagNamespace": true, + "TagPackedType": true, + "TagPartialUnit": true, + "TagPointerType": true, + "TagPtrToMemberType": true, + "TagReferenceType": true, + "TagRestrictType": true, + "TagRvalueReferenceType": true, + "TagSetType": true, + "TagSharedType": true, + "TagStringType": true, + "TagStructType": true, + "TagSubprogram": true, + "TagSubrangeType": true, + "TagSubroutineType": true, + "TagTemplateAlias": true, + "TagTemplateTypeParameter": true, + "TagTemplateValueParameter": true, + "TagThrownType": true, + "TagTryDwarfBlock": true, + "TagTypeUnit": true, + "TagTypedef": true, + "TagUnionType": true, + "TagUnspecifiedParameters": true, + "TagUnspecifiedType": true, + "TagVariable": true, + "TagVariant": true, + "TagVariantPart": true, + "TagVolatileType": true, + "TagWithStmt": true, + "Type": true, + "TypedefType": true, + "UcharType": true, + "UintType": true, + "UnspecifiedType": true, + "VoidType": true, + }, + "debug/elf": map[string]bool{ + "ARM_MAGIC_TRAMP_NUMBER": true, + "COMPRESS_HIOS": true, + "COMPRESS_HIPROC": true, + "COMPRESS_LOOS": true, + "COMPRESS_LOPROC": true, + "COMPRESS_ZLIB": true, + "Chdr32": true, + "Chdr64": true, + "Class": true, + "CompressionType": true, + "DF_BIND_NOW": true, + "DF_ORIGIN": true, + "DF_STATIC_TLS": true, + "DF_SYMBOLIC": true, + "DF_TEXTREL": true, + "DT_BIND_NOW": true, + "DT_DEBUG": true, + "DT_ENCODING": true, + "DT_FINI": true, + "DT_FINI_ARRAY": true, + "DT_FINI_ARRAYSZ": true, + "DT_FLAGS": true, + "DT_HASH": true, + "DT_HIOS": true, + "DT_HIPROC": true, + "DT_INIT": true, + "DT_INIT_ARRAY": true, + "DT_INIT_ARRAYSZ": true, + "DT_JMPREL": true, + "DT_LOOS": true, + "DT_LOPROC": true, + "DT_NEEDED": true, + "DT_NULL": true, + "DT_PLTGOT": true, + "DT_PLTREL": true, + "DT_PLTRELSZ": true, + "DT_PREINIT_ARRAY": true, + "DT_PREINIT_ARRAYSZ": true, + "DT_REL": true, + "DT_RELA": true, + "DT_RELAENT": true, + "DT_RELASZ": true, + "DT_RELENT": true, + "DT_RELSZ": true, + "DT_RPATH": true, + "DT_RUNPATH": true, + "DT_SONAME": true, + "DT_STRSZ": true, + "DT_STRTAB": true, + "DT_SYMBOLIC": true, + "DT_SYMENT": true, + "DT_SYMTAB": true, + "DT_TEXTREL": true, + "DT_VERNEED": true, + "DT_VERNEEDNUM": true, + "DT_VERSYM": true, + "Data": true, + "Dyn32": true, + "Dyn64": true, + "DynFlag": true, + "DynTag": true, + "EI_ABIVERSION": true, + "EI_CLASS": true, + "EI_DATA": true, + "EI_NIDENT": true, + "EI_OSABI": true, + "EI_PAD": true, + "EI_VERSION": true, + "ELFCLASS32": true, + "ELFCLASS64": true, + "ELFCLASSNONE": true, + "ELFDATA2LSB": true, + "ELFDATA2MSB": true, + "ELFDATANONE": true, + "ELFMAG": true, + "ELFOSABI_86OPEN": true, + "ELFOSABI_AIX": true, + "ELFOSABI_ARM": true, + "ELFOSABI_AROS": true, + "ELFOSABI_CLOUDABI": true, + "ELFOSABI_FENIXOS": true, + "ELFOSABI_FREEBSD": true, + "ELFOSABI_HPUX": true, + "ELFOSABI_HURD": true, + "ELFOSABI_IRIX": true, + "ELFOSABI_LINUX": true, + "ELFOSABI_MODESTO": true, + "ELFOSABI_NETBSD": true, + "ELFOSABI_NONE": true, + "ELFOSABI_NSK": true, + "ELFOSABI_OPENBSD": true, + "ELFOSABI_OPENVMS": true, + "ELFOSABI_SOLARIS": true, + "ELFOSABI_STANDALONE": true, + "ELFOSABI_TRU64": true, + "EM_386": true, + "EM_486": true, + "EM_56800EX": true, + "EM_68HC05": true, + "EM_68HC08": true, + "EM_68HC11": true, + "EM_68HC12": true, + "EM_68HC16": true, + "EM_68K": true, + "EM_78KOR": true, + "EM_8051": true, + "EM_860": true, + "EM_88K": true, + "EM_960": true, + "EM_AARCH64": true, + "EM_ALPHA": true, + "EM_ALPHA_STD": true, + "EM_ALTERA_NIOS2": true, + "EM_AMDGPU": true, + "EM_ARC": true, + "EM_ARCA": true, + "EM_ARC_COMPACT": true, + "EM_ARC_COMPACT2": true, + "EM_ARM": true, + "EM_AVR": true, + "EM_AVR32": true, + "EM_BA1": true, + "EM_BA2": true, + "EM_BLACKFIN": true, + "EM_BPF": true, + "EM_C166": true, + "EM_CDP": true, + "EM_CE": true, + "EM_CLOUDSHIELD": true, + "EM_COGE": true, + "EM_COLDFIRE": true, + "EM_COOL": true, + "EM_COREA_1ST": true, + "EM_COREA_2ND": true, + "EM_CR": true, + "EM_CR16": true, + "EM_CRAYNV2": true, + "EM_CRIS": true, + "EM_CRX": true, + "EM_CSR_KALIMBA": true, + "EM_CUDA": true, + "EM_CYPRESS_M8C": true, + "EM_D10V": true, + "EM_D30V": true, + "EM_DSP24": true, + "EM_DSPIC30F": true, + "EM_DXP": true, + "EM_ECOG1": true, + "EM_ECOG16": true, + "EM_ECOG1X": true, + "EM_ECOG2": true, + "EM_ETPU": true, + "EM_EXCESS": true, + "EM_F2MC16": true, + "EM_FIREPATH": true, + "EM_FR20": true, + "EM_FR30": true, + "EM_FT32": true, + "EM_FX66": true, + "EM_H8S": true, + "EM_H8_300": true, + "EM_H8_300H": true, + "EM_H8_500": true, + "EM_HUANY": true, + "EM_IA_64": true, + "EM_INTEL205": true, + "EM_INTEL206": true, + "EM_INTEL207": true, + "EM_INTEL208": true, + "EM_INTEL209": true, + "EM_IP2K": true, + "EM_JAVELIN": true, + "EM_K10M": true, + "EM_KM32": true, + "EM_KMX16": true, + "EM_KMX32": true, + "EM_KMX8": true, + "EM_KVARC": true, + "EM_L10M": true, + "EM_LANAI": true, + "EM_LATTICEMICO32": true, + "EM_M16C": true, + "EM_M32": true, + "EM_M32C": true, + "EM_M32R": true, + "EM_MANIK": true, + "EM_MAX": true, + "EM_MAXQ30": true, + "EM_MCHP_PIC": true, + "EM_MCST_ELBRUS": true, + "EM_ME16": true, + "EM_METAG": true, + "EM_MICROBLAZE": true, + "EM_MIPS": true, + "EM_MIPS_RS3_LE": true, + "EM_MIPS_RS4_BE": true, + "EM_MIPS_X": true, + "EM_MMA": true, + "EM_MMDSP_PLUS": true, + "EM_MMIX": true, + "EM_MN10200": true, + "EM_MN10300": true, + "EM_MOXIE": true, + "EM_MSP430": true, + "EM_NCPU": true, + "EM_NDR1": true, + "EM_NDS32": true, + "EM_NONE": true, + "EM_NORC": true, + "EM_NS32K": true, + "EM_OPEN8": true, + "EM_OPENRISC": true, + "EM_PARISC": true, + "EM_PCP": true, + "EM_PDP10": true, + "EM_PDP11": true, + "EM_PDSP": true, + "EM_PJ": true, + "EM_PPC": true, + "EM_PPC64": true, + "EM_PRISM": true, + "EM_QDSP6": true, + "EM_R32C": true, + "EM_RCE": true, + "EM_RH32": true, + "EM_RISCV": true, + "EM_RL78": true, + "EM_RS08": true, + "EM_RX": true, + "EM_S370": true, + "EM_S390": true, + "EM_SCORE7": true, + "EM_SEP": true, + "EM_SE_C17": true, + "EM_SE_C33": true, + "EM_SH": true, + "EM_SHARC": true, + "EM_SLE9X": true, + "EM_SNP1K": true, + "EM_SPARC": true, + "EM_SPARC32PLUS": true, + "EM_SPARCV9": true, + "EM_ST100": true, + "EM_ST19": true, + "EM_ST200": true, + "EM_ST7": true, + "EM_ST9PLUS": true, + "EM_STARCORE": true, + "EM_STM8": true, + "EM_STXP7X": true, + "EM_SVX": true, + "EM_TILE64": true, + "EM_TILEGX": true, + "EM_TILEPRO": true, + "EM_TINYJ": true, + "EM_TI_ARP32": true, + "EM_TI_C2000": true, + "EM_TI_C5500": true, + "EM_TI_C6000": true, + "EM_TI_PRU": true, + "EM_TMM_GPP": true, + "EM_TPC": true, + "EM_TRICORE": true, + "EM_TRIMEDIA": true, + "EM_TSK3000": true, + "EM_UNICORE": true, + "EM_V800": true, + "EM_V850": true, + "EM_VAX": true, + "EM_VIDEOCORE": true, + "EM_VIDEOCORE3": true, + "EM_VIDEOCORE5": true, + "EM_VISIUM": true, + "EM_VPP500": true, + "EM_X86_64": true, + "EM_XCORE": true, + "EM_XGATE": true, + "EM_XIMO16": true, + "EM_XTENSA": true, + "EM_Z80": true, + "EM_ZSP": true, + "ET_CORE": true, + "ET_DYN": true, + "ET_EXEC": true, + "ET_HIOS": true, + "ET_HIPROC": true, + "ET_LOOS": true, + "ET_LOPROC": true, + "ET_NONE": true, + "ET_REL": true, + "EV_CURRENT": true, + "EV_NONE": true, + "ErrNoSymbols": true, + "File": true, + "FileHeader": true, + "FormatError": true, + "Header32": true, + "Header64": true, + "ImportedSymbol": true, + "Machine": true, + "NT_FPREGSET": true, + "NT_PRPSINFO": true, + "NT_PRSTATUS": true, + "NType": true, + "NewFile": true, + "OSABI": true, + "Open": true, + "PF_MASKOS": true, + "PF_MASKPROC": true, + "PF_R": true, + "PF_W": true, + "PF_X": true, + "PT_DYNAMIC": true, + "PT_HIOS": true, + "PT_HIPROC": true, + "PT_INTERP": true, + "PT_LOAD": true, + "PT_LOOS": true, + "PT_LOPROC": true, + "PT_NOTE": true, + "PT_NULL": true, + "PT_PHDR": true, + "PT_SHLIB": true, + "PT_TLS": true, + "Prog": true, + "Prog32": true, + "Prog64": true, + "ProgFlag": true, + "ProgHeader": true, + "ProgType": true, + "R_386": true, + "R_386_16": true, + "R_386_32": true, + "R_386_32PLT": true, + "R_386_8": true, + "R_386_COPY": true, + "R_386_GLOB_DAT": true, + "R_386_GOT32": true, + "R_386_GOT32X": true, + "R_386_GOTOFF": true, + "R_386_GOTPC": true, + "R_386_IRELATIVE": true, + "R_386_JMP_SLOT": true, + "R_386_NONE": true, + "R_386_PC16": true, + "R_386_PC32": true, + "R_386_PC8": true, + "R_386_PLT32": true, + "R_386_RELATIVE": true, + "R_386_SIZE32": true, + "R_386_TLS_DESC": true, + "R_386_TLS_DESC_CALL": true, + "R_386_TLS_DTPMOD32": true, + "R_386_TLS_DTPOFF32": true, + "R_386_TLS_GD": true, + "R_386_TLS_GD_32": true, + "R_386_TLS_GD_CALL": true, + "R_386_TLS_GD_POP": true, + "R_386_TLS_GD_PUSH": true, + "R_386_TLS_GOTDESC": true, + "R_386_TLS_GOTIE": true, + "R_386_TLS_IE": true, + "R_386_TLS_IE_32": true, + "R_386_TLS_LDM": true, + "R_386_TLS_LDM_32": true, + "R_386_TLS_LDM_CALL": true, + "R_386_TLS_LDM_POP": true, + "R_386_TLS_LDM_PUSH": true, + "R_386_TLS_LDO_32": true, + "R_386_TLS_LE": true, + "R_386_TLS_LE_32": true, + "R_386_TLS_TPOFF": true, + "R_386_TLS_TPOFF32": true, + "R_390": true, + "R_390_12": true, + "R_390_16": true, + "R_390_20": true, + "R_390_32": true, + "R_390_64": true, + "R_390_8": true, + "R_390_COPY": true, + "R_390_GLOB_DAT": true, + "R_390_GOT12": true, + "R_390_GOT16": true, + "R_390_GOT20": true, + "R_390_GOT32": true, + "R_390_GOT64": true, + "R_390_GOTENT": true, + "R_390_GOTOFF": true, + "R_390_GOTOFF16": true, + "R_390_GOTOFF64": true, + "R_390_GOTPC": true, + "R_390_GOTPCDBL": true, + "R_390_GOTPLT12": true, + "R_390_GOTPLT16": true, + "R_390_GOTPLT20": true, + "R_390_GOTPLT32": true, + "R_390_GOTPLT64": true, + "R_390_GOTPLTENT": true, + "R_390_GOTPLTOFF16": true, + "R_390_GOTPLTOFF32": true, + "R_390_GOTPLTOFF64": true, + "R_390_JMP_SLOT": true, + "R_390_NONE": true, + "R_390_PC16": true, + "R_390_PC16DBL": true, + "R_390_PC32": true, + "R_390_PC32DBL": true, + "R_390_PC64": true, + "R_390_PLT16DBL": true, + "R_390_PLT32": true, + "R_390_PLT32DBL": true, + "R_390_PLT64": true, + "R_390_RELATIVE": true, + "R_390_TLS_DTPMOD": true, + "R_390_TLS_DTPOFF": true, + "R_390_TLS_GD32": true, + "R_390_TLS_GD64": true, + "R_390_TLS_GDCALL": true, + "R_390_TLS_GOTIE12": true, + "R_390_TLS_GOTIE20": true, + "R_390_TLS_GOTIE32": true, + "R_390_TLS_GOTIE64": true, + "R_390_TLS_IE32": true, + "R_390_TLS_IE64": true, + "R_390_TLS_IEENT": true, + "R_390_TLS_LDCALL": true, + "R_390_TLS_LDM32": true, + "R_390_TLS_LDM64": true, + "R_390_TLS_LDO32": true, + "R_390_TLS_LDO64": true, + "R_390_TLS_LE32": true, + "R_390_TLS_LE64": true, + "R_390_TLS_LOAD": true, + "R_390_TLS_TPOFF": true, + "R_AARCH64": true, + "R_AARCH64_ABS16": true, + "R_AARCH64_ABS32": true, + "R_AARCH64_ABS64": true, + "R_AARCH64_ADD_ABS_LO12_NC": true, + "R_AARCH64_ADR_GOT_PAGE": true, + "R_AARCH64_ADR_PREL_LO21": true, + "R_AARCH64_ADR_PREL_PG_HI21": true, + "R_AARCH64_ADR_PREL_PG_HI21_NC": true, + "R_AARCH64_CALL26": true, + "R_AARCH64_CONDBR19": true, + "R_AARCH64_COPY": true, + "R_AARCH64_GLOB_DAT": true, + "R_AARCH64_GOT_LD_PREL19": true, + "R_AARCH64_IRELATIVE": true, + "R_AARCH64_JUMP26": true, + "R_AARCH64_JUMP_SLOT": true, + "R_AARCH64_LD64_GOTOFF_LO15": true, + "R_AARCH64_LD64_GOTPAGE_LO15": true, + "R_AARCH64_LD64_GOT_LO12_NC": true, + "R_AARCH64_LDST128_ABS_LO12_NC": true, + "R_AARCH64_LDST16_ABS_LO12_NC": true, + "R_AARCH64_LDST32_ABS_LO12_NC": true, + "R_AARCH64_LDST64_ABS_LO12_NC": true, + "R_AARCH64_LDST8_ABS_LO12_NC": true, + "R_AARCH64_LD_PREL_LO19": true, + "R_AARCH64_MOVW_SABS_G0": true, + "R_AARCH64_MOVW_SABS_G1": true, + "R_AARCH64_MOVW_SABS_G2": true, + "R_AARCH64_MOVW_UABS_G0": true, + "R_AARCH64_MOVW_UABS_G0_NC": true, + "R_AARCH64_MOVW_UABS_G1": true, + "R_AARCH64_MOVW_UABS_G1_NC": true, + "R_AARCH64_MOVW_UABS_G2": true, + "R_AARCH64_MOVW_UABS_G2_NC": true, + "R_AARCH64_MOVW_UABS_G3": true, + "R_AARCH64_NONE": true, + "R_AARCH64_NULL": true, + "R_AARCH64_P32_ABS16": true, + "R_AARCH64_P32_ABS32": true, + "R_AARCH64_P32_ADD_ABS_LO12_NC": true, + "R_AARCH64_P32_ADR_GOT_PAGE": true, + "R_AARCH64_P32_ADR_PREL_LO21": true, + "R_AARCH64_P32_ADR_PREL_PG_HI21": true, + "R_AARCH64_P32_CALL26": true, + "R_AARCH64_P32_CONDBR19": true, + "R_AARCH64_P32_COPY": true, + "R_AARCH64_P32_GLOB_DAT": true, + "R_AARCH64_P32_GOT_LD_PREL19": true, + "R_AARCH64_P32_IRELATIVE": true, + "R_AARCH64_P32_JUMP26": true, + "R_AARCH64_P32_JUMP_SLOT": true, + "R_AARCH64_P32_LD32_GOT_LO12_NC": true, + "R_AARCH64_P32_LDST128_ABS_LO12_NC": true, + "R_AARCH64_P32_LDST16_ABS_LO12_NC": true, + "R_AARCH64_P32_LDST32_ABS_LO12_NC": true, + "R_AARCH64_P32_LDST64_ABS_LO12_NC": true, + "R_AARCH64_P32_LDST8_ABS_LO12_NC": true, + "R_AARCH64_P32_LD_PREL_LO19": true, + "R_AARCH64_P32_MOVW_SABS_G0": true, + "R_AARCH64_P32_MOVW_UABS_G0": true, + "R_AARCH64_P32_MOVW_UABS_G0_NC": true, + "R_AARCH64_P32_MOVW_UABS_G1": true, + "R_AARCH64_P32_PREL16": true, + "R_AARCH64_P32_PREL32": true, + "R_AARCH64_P32_RELATIVE": true, + "R_AARCH64_P32_TLSDESC": true, + "R_AARCH64_P32_TLSDESC_ADD_LO12_NC": true, + "R_AARCH64_P32_TLSDESC_ADR_PAGE21": true, + "R_AARCH64_P32_TLSDESC_ADR_PREL21": true, + "R_AARCH64_P32_TLSDESC_CALL": true, + "R_AARCH64_P32_TLSDESC_LD32_LO12_NC": true, + "R_AARCH64_P32_TLSDESC_LD_PREL19": true, + "R_AARCH64_P32_TLSGD_ADD_LO12_NC": true, + "R_AARCH64_P32_TLSGD_ADR_PAGE21": true, + "R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21": true, + "R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC": true, + "R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19": true, + "R_AARCH64_P32_TLSLE_ADD_TPREL_HI12": true, + "R_AARCH64_P32_TLSLE_ADD_TPREL_LO12": true, + "R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC": true, + "R_AARCH64_P32_TLSLE_MOVW_TPREL_G0": true, + "R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC": true, + "R_AARCH64_P32_TLSLE_MOVW_TPREL_G1": true, + "R_AARCH64_P32_TLS_DTPMOD": true, + "R_AARCH64_P32_TLS_DTPREL": true, + "R_AARCH64_P32_TLS_TPREL": true, + "R_AARCH64_P32_TSTBR14": true, + "R_AARCH64_PREL16": true, + "R_AARCH64_PREL32": true, + "R_AARCH64_PREL64": true, + "R_AARCH64_RELATIVE": true, + "R_AARCH64_TLSDESC": true, + "R_AARCH64_TLSDESC_ADD": true, + "R_AARCH64_TLSDESC_ADD_LO12_NC": true, + "R_AARCH64_TLSDESC_ADR_PAGE21": true, + "R_AARCH64_TLSDESC_ADR_PREL21": true, + "R_AARCH64_TLSDESC_CALL": true, + "R_AARCH64_TLSDESC_LD64_LO12_NC": true, + "R_AARCH64_TLSDESC_LDR": true, + "R_AARCH64_TLSDESC_LD_PREL19": true, + "R_AARCH64_TLSDESC_OFF_G0_NC": true, + "R_AARCH64_TLSDESC_OFF_G1": true, + "R_AARCH64_TLSGD_ADD_LO12_NC": true, + "R_AARCH64_TLSGD_ADR_PAGE21": true, + "R_AARCH64_TLSGD_ADR_PREL21": true, + "R_AARCH64_TLSGD_MOVW_G0_NC": true, + "R_AARCH64_TLSGD_MOVW_G1": true, + "R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21": true, + "R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC": true, + "R_AARCH64_TLSIE_LD_GOTTPREL_PREL19": true, + "R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC": true, + "R_AARCH64_TLSIE_MOVW_GOTTPREL_G1": true, + "R_AARCH64_TLSLD_ADR_PAGE21": true, + "R_AARCH64_TLSLD_ADR_PREL21": true, + "R_AARCH64_TLSLD_LDST128_DTPREL_LO12": true, + "R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC": true, + "R_AARCH64_TLSLE_ADD_TPREL_HI12": true, + "R_AARCH64_TLSLE_ADD_TPREL_LO12": true, + "R_AARCH64_TLSLE_ADD_TPREL_LO12_NC": true, + "R_AARCH64_TLSLE_LDST128_TPREL_LO12": true, + "R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC": true, + "R_AARCH64_TLSLE_MOVW_TPREL_G0": true, + "R_AARCH64_TLSLE_MOVW_TPREL_G0_NC": true, + "R_AARCH64_TLSLE_MOVW_TPREL_G1": true, + "R_AARCH64_TLSLE_MOVW_TPREL_G1_NC": true, + "R_AARCH64_TLSLE_MOVW_TPREL_G2": true, + "R_AARCH64_TLS_DTPMOD64": true, + "R_AARCH64_TLS_DTPREL64": true, + "R_AARCH64_TLS_TPREL64": true, + "R_AARCH64_TSTBR14": true, + "R_ALPHA": true, + "R_ALPHA_BRADDR": true, + "R_ALPHA_COPY": true, + "R_ALPHA_GLOB_DAT": true, + "R_ALPHA_GPDISP": true, + "R_ALPHA_GPREL32": true, + "R_ALPHA_GPRELHIGH": true, + "R_ALPHA_GPRELLOW": true, + "R_ALPHA_GPVALUE": true, + "R_ALPHA_HINT": true, + "R_ALPHA_IMMED_BR_HI32": true, + "R_ALPHA_IMMED_GP_16": true, + "R_ALPHA_IMMED_GP_HI32": true, + "R_ALPHA_IMMED_LO32": true, + "R_ALPHA_IMMED_SCN_HI32": true, + "R_ALPHA_JMP_SLOT": true, + "R_ALPHA_LITERAL": true, + "R_ALPHA_LITUSE": true, + "R_ALPHA_NONE": true, + "R_ALPHA_OP_PRSHIFT": true, + "R_ALPHA_OP_PSUB": true, + "R_ALPHA_OP_PUSH": true, + "R_ALPHA_OP_STORE": true, + "R_ALPHA_REFLONG": true, + "R_ALPHA_REFQUAD": true, + "R_ALPHA_RELATIVE": true, + "R_ALPHA_SREL16": true, + "R_ALPHA_SREL32": true, + "R_ALPHA_SREL64": true, + "R_ARM": true, + "R_ARM_ABS12": true, + "R_ARM_ABS16": true, + "R_ARM_ABS32": true, + "R_ARM_ABS32_NOI": true, + "R_ARM_ABS8": true, + "R_ARM_ALU_PCREL_15_8": true, + "R_ARM_ALU_PCREL_23_15": true, + "R_ARM_ALU_PCREL_7_0": true, + "R_ARM_ALU_PC_G0": true, + "R_ARM_ALU_PC_G0_NC": true, + "R_ARM_ALU_PC_G1": true, + "R_ARM_ALU_PC_G1_NC": true, + "R_ARM_ALU_PC_G2": true, + "R_ARM_ALU_SBREL_19_12_NC": true, + "R_ARM_ALU_SBREL_27_20_CK": true, + "R_ARM_ALU_SB_G0": true, + "R_ARM_ALU_SB_G0_NC": true, + "R_ARM_ALU_SB_G1": true, + "R_ARM_ALU_SB_G1_NC": true, + "R_ARM_ALU_SB_G2": true, + "R_ARM_AMP_VCALL9": true, + "R_ARM_BASE_ABS": true, + "R_ARM_CALL": true, + "R_ARM_COPY": true, + "R_ARM_GLOB_DAT": true, + "R_ARM_GNU_VTENTRY": true, + "R_ARM_GNU_VTINHERIT": true, + "R_ARM_GOT32": true, + "R_ARM_GOTOFF": true, + "R_ARM_GOTOFF12": true, + "R_ARM_GOTPC": true, + "R_ARM_GOTRELAX": true, + "R_ARM_GOT_ABS": true, + "R_ARM_GOT_BREL12": true, + "R_ARM_GOT_PREL": true, + "R_ARM_IRELATIVE": true, + "R_ARM_JUMP24": true, + "R_ARM_JUMP_SLOT": true, + "R_ARM_LDC_PC_G0": true, + "R_ARM_LDC_PC_G1": true, + "R_ARM_LDC_PC_G2": true, + "R_ARM_LDC_SB_G0": true, + "R_ARM_LDC_SB_G1": true, + "R_ARM_LDC_SB_G2": true, + "R_ARM_LDRS_PC_G0": true, + "R_ARM_LDRS_PC_G1": true, + "R_ARM_LDRS_PC_G2": true, + "R_ARM_LDRS_SB_G0": true, + "R_ARM_LDRS_SB_G1": true, + "R_ARM_LDRS_SB_G2": true, + "R_ARM_LDR_PC_G1": true, + "R_ARM_LDR_PC_G2": true, + "R_ARM_LDR_SBREL_11_10_NC": true, + "R_ARM_LDR_SB_G0": true, + "R_ARM_LDR_SB_G1": true, + "R_ARM_LDR_SB_G2": true, + "R_ARM_ME_TOO": true, + "R_ARM_MOVT_ABS": true, + "R_ARM_MOVT_BREL": true, + "R_ARM_MOVT_PREL": true, + "R_ARM_MOVW_ABS_NC": true, + "R_ARM_MOVW_BREL": true, + "R_ARM_MOVW_BREL_NC": true, + "R_ARM_MOVW_PREL_NC": true, + "R_ARM_NONE": true, + "R_ARM_PC13": true, + "R_ARM_PC24": true, + "R_ARM_PLT32": true, + "R_ARM_PLT32_ABS": true, + "R_ARM_PREL31": true, + "R_ARM_PRIVATE_0": true, + "R_ARM_PRIVATE_1": true, + "R_ARM_PRIVATE_10": true, + "R_ARM_PRIVATE_11": true, + "R_ARM_PRIVATE_12": true, + "R_ARM_PRIVATE_13": true, + "R_ARM_PRIVATE_14": true, + "R_ARM_PRIVATE_15": true, + "R_ARM_PRIVATE_2": true, + "R_ARM_PRIVATE_3": true, + "R_ARM_PRIVATE_4": true, + "R_ARM_PRIVATE_5": true, + "R_ARM_PRIVATE_6": true, + "R_ARM_PRIVATE_7": true, + "R_ARM_PRIVATE_8": true, + "R_ARM_PRIVATE_9": true, + "R_ARM_RABS32": true, + "R_ARM_RBASE": true, + "R_ARM_REL32": true, + "R_ARM_REL32_NOI": true, + "R_ARM_RELATIVE": true, + "R_ARM_RPC24": true, + "R_ARM_RREL32": true, + "R_ARM_RSBREL32": true, + "R_ARM_RXPC25": true, + "R_ARM_SBREL31": true, + "R_ARM_SBREL32": true, + "R_ARM_SWI24": true, + "R_ARM_TARGET1": true, + "R_ARM_TARGET2": true, + "R_ARM_THM_ABS5": true, + "R_ARM_THM_ALU_ABS_G0_NC": true, + "R_ARM_THM_ALU_ABS_G1_NC": true, + "R_ARM_THM_ALU_ABS_G2_NC": true, + "R_ARM_THM_ALU_ABS_G3": true, + "R_ARM_THM_ALU_PREL_11_0": true, + "R_ARM_THM_GOT_BREL12": true, + "R_ARM_THM_JUMP11": true, + "R_ARM_THM_JUMP19": true, + "R_ARM_THM_JUMP24": true, + "R_ARM_THM_JUMP6": true, + "R_ARM_THM_JUMP8": true, + "R_ARM_THM_MOVT_ABS": true, + "R_ARM_THM_MOVT_BREL": true, + "R_ARM_THM_MOVT_PREL": true, + "R_ARM_THM_MOVW_ABS_NC": true, + "R_ARM_THM_MOVW_BREL": true, + "R_ARM_THM_MOVW_BREL_NC": true, + "R_ARM_THM_MOVW_PREL_NC": true, + "R_ARM_THM_PC12": true, + "R_ARM_THM_PC22": true, + "R_ARM_THM_PC8": true, + "R_ARM_THM_RPC22": true, + "R_ARM_THM_SWI8": true, + "R_ARM_THM_TLS_CALL": true, + "R_ARM_THM_TLS_DESCSEQ16": true, + "R_ARM_THM_TLS_DESCSEQ32": true, + "R_ARM_THM_XPC22": true, + "R_ARM_TLS_CALL": true, + "R_ARM_TLS_DESCSEQ": true, + "R_ARM_TLS_DTPMOD32": true, + "R_ARM_TLS_DTPOFF32": true, + "R_ARM_TLS_GD32": true, + "R_ARM_TLS_GOTDESC": true, + "R_ARM_TLS_IE12GP": true, + "R_ARM_TLS_IE32": true, + "R_ARM_TLS_LDM32": true, + "R_ARM_TLS_LDO12": true, + "R_ARM_TLS_LDO32": true, + "R_ARM_TLS_LE12": true, + "R_ARM_TLS_LE32": true, + "R_ARM_TLS_TPOFF32": true, + "R_ARM_V4BX": true, + "R_ARM_XPC25": true, + "R_INFO": true, + "R_INFO32": true, + "R_MIPS": true, + "R_MIPS_16": true, + "R_MIPS_26": true, + "R_MIPS_32": true, + "R_MIPS_64": true, + "R_MIPS_ADD_IMMEDIATE": true, + "R_MIPS_CALL16": true, + "R_MIPS_CALL_HI16": true, + "R_MIPS_CALL_LO16": true, + "R_MIPS_DELETE": true, + "R_MIPS_GOT16": true, + "R_MIPS_GOT_DISP": true, + "R_MIPS_GOT_HI16": true, + "R_MIPS_GOT_LO16": true, + "R_MIPS_GOT_OFST": true, + "R_MIPS_GOT_PAGE": true, + "R_MIPS_GPREL16": true, + "R_MIPS_GPREL32": true, + "R_MIPS_HI16": true, + "R_MIPS_HIGHER": true, + "R_MIPS_HIGHEST": true, + "R_MIPS_INSERT_A": true, + "R_MIPS_INSERT_B": true, + "R_MIPS_JALR": true, + "R_MIPS_LITERAL": true, + "R_MIPS_LO16": true, + "R_MIPS_NONE": true, + "R_MIPS_PC16": true, + "R_MIPS_PJUMP": true, + "R_MIPS_REL16": true, + "R_MIPS_REL32": true, + "R_MIPS_RELGOT": true, + "R_MIPS_SCN_DISP": true, + "R_MIPS_SHIFT5": true, + "R_MIPS_SHIFT6": true, + "R_MIPS_SUB": true, + "R_MIPS_TLS_DTPMOD32": true, + "R_MIPS_TLS_DTPMOD64": true, + "R_MIPS_TLS_DTPREL32": true, + "R_MIPS_TLS_DTPREL64": true, + "R_MIPS_TLS_DTPREL_HI16": true, + "R_MIPS_TLS_DTPREL_LO16": true, + "R_MIPS_TLS_GD": true, + "R_MIPS_TLS_GOTTPREL": true, + "R_MIPS_TLS_LDM": true, + "R_MIPS_TLS_TPREL32": true, + "R_MIPS_TLS_TPREL64": true, + "R_MIPS_TLS_TPREL_HI16": true, + "R_MIPS_TLS_TPREL_LO16": true, + "R_PPC": true, + "R_PPC64": true, + "R_PPC64_ADDR14": true, + "R_PPC64_ADDR14_BRNTAKEN": true, + "R_PPC64_ADDR14_BRTAKEN": true, + "R_PPC64_ADDR16": true, + "R_PPC64_ADDR16_DS": true, + "R_PPC64_ADDR16_HA": true, + "R_PPC64_ADDR16_HI": true, + "R_PPC64_ADDR16_HIGH": true, + "R_PPC64_ADDR16_HIGHA": true, + "R_PPC64_ADDR16_HIGHER": true, + "R_PPC64_ADDR16_HIGHERA": true, + "R_PPC64_ADDR16_HIGHEST": true, + "R_PPC64_ADDR16_HIGHESTA": true, + "R_PPC64_ADDR16_LO": true, + "R_PPC64_ADDR16_LO_DS": true, + "R_PPC64_ADDR24": true, + "R_PPC64_ADDR32": true, + "R_PPC64_ADDR64": true, + "R_PPC64_ADDR64_LOCAL": true, + "R_PPC64_DTPMOD64": true, + "R_PPC64_DTPREL16": true, + "R_PPC64_DTPREL16_DS": true, + "R_PPC64_DTPREL16_HA": true, + "R_PPC64_DTPREL16_HI": true, + "R_PPC64_DTPREL16_HIGH": true, + "R_PPC64_DTPREL16_HIGHA": true, + "R_PPC64_DTPREL16_HIGHER": true, + "R_PPC64_DTPREL16_HIGHERA": true, + "R_PPC64_DTPREL16_HIGHEST": true, + "R_PPC64_DTPREL16_HIGHESTA": true, + "R_PPC64_DTPREL16_LO": true, + "R_PPC64_DTPREL16_LO_DS": true, + "R_PPC64_DTPREL64": true, + "R_PPC64_ENTRY": true, + "R_PPC64_GOT16": true, + "R_PPC64_GOT16_DS": true, + "R_PPC64_GOT16_HA": true, + "R_PPC64_GOT16_HI": true, + "R_PPC64_GOT16_LO": true, + "R_PPC64_GOT16_LO_DS": true, + "R_PPC64_GOT_DTPREL16_DS": true, + "R_PPC64_GOT_DTPREL16_HA": true, + "R_PPC64_GOT_DTPREL16_HI": true, + "R_PPC64_GOT_DTPREL16_LO_DS": true, + "R_PPC64_GOT_TLSGD16": true, + "R_PPC64_GOT_TLSGD16_HA": true, + "R_PPC64_GOT_TLSGD16_HI": true, + "R_PPC64_GOT_TLSGD16_LO": true, + "R_PPC64_GOT_TLSLD16": true, + "R_PPC64_GOT_TLSLD16_HA": true, + "R_PPC64_GOT_TLSLD16_HI": true, + "R_PPC64_GOT_TLSLD16_LO": true, + "R_PPC64_GOT_TPREL16_DS": true, + "R_PPC64_GOT_TPREL16_HA": true, + "R_PPC64_GOT_TPREL16_HI": true, + "R_PPC64_GOT_TPREL16_LO_DS": true, + "R_PPC64_IRELATIVE": true, + "R_PPC64_JMP_IREL": true, + "R_PPC64_JMP_SLOT": true, + "R_PPC64_NONE": true, + "R_PPC64_PLT16_LO_DS": true, + "R_PPC64_PLTGOT16": true, + "R_PPC64_PLTGOT16_DS": true, + "R_PPC64_PLTGOT16_HA": true, + "R_PPC64_PLTGOT16_HI": true, + "R_PPC64_PLTGOT16_LO": true, + "R_PPC64_PLTGOT_LO_DS": true, + "R_PPC64_REL14": true, + "R_PPC64_REL14_BRNTAKEN": true, + "R_PPC64_REL14_BRTAKEN": true, + "R_PPC64_REL16": true, + "R_PPC64_REL16DX_HA": true, + "R_PPC64_REL16_HA": true, + "R_PPC64_REL16_HI": true, + "R_PPC64_REL16_LO": true, + "R_PPC64_REL24": true, + "R_PPC64_REL24_NOTOC": true, + "R_PPC64_REL32": true, + "R_PPC64_REL64": true, + "R_PPC64_SECTOFF_DS": true, + "R_PPC64_SECTOFF_LO_DS": true, + "R_PPC64_TLS": true, + "R_PPC64_TLSGD": true, + "R_PPC64_TLSLD": true, + "R_PPC64_TOC": true, + "R_PPC64_TOC16": true, + "R_PPC64_TOC16_DS": true, + "R_PPC64_TOC16_HA": true, + "R_PPC64_TOC16_HI": true, + "R_PPC64_TOC16_LO": true, + "R_PPC64_TOC16_LO_DS": true, + "R_PPC64_TOCSAVE": true, + "R_PPC64_TPREL16": true, + "R_PPC64_TPREL16_DS": true, + "R_PPC64_TPREL16_HA": true, + "R_PPC64_TPREL16_HI": true, + "R_PPC64_TPREL16_HIGH": true, + "R_PPC64_TPREL16_HIGHA": true, + "R_PPC64_TPREL16_HIGHER": true, + "R_PPC64_TPREL16_HIGHERA": true, + "R_PPC64_TPREL16_HIGHEST": true, + "R_PPC64_TPREL16_HIGHESTA": true, + "R_PPC64_TPREL16_LO": true, + "R_PPC64_TPREL16_LO_DS": true, + "R_PPC64_TPREL64": true, + "R_PPC_ADDR14": true, + "R_PPC_ADDR14_BRNTAKEN": true, + "R_PPC_ADDR14_BRTAKEN": true, + "R_PPC_ADDR16": true, + "R_PPC_ADDR16_HA": true, + "R_PPC_ADDR16_HI": true, + "R_PPC_ADDR16_LO": true, + "R_PPC_ADDR24": true, + "R_PPC_ADDR32": true, + "R_PPC_COPY": true, + "R_PPC_DTPMOD32": true, + "R_PPC_DTPREL16": true, + "R_PPC_DTPREL16_HA": true, + "R_PPC_DTPREL16_HI": true, + "R_PPC_DTPREL16_LO": true, + "R_PPC_DTPREL32": true, + "R_PPC_EMB_BIT_FLD": true, + "R_PPC_EMB_MRKREF": true, + "R_PPC_EMB_NADDR16": true, + "R_PPC_EMB_NADDR16_HA": true, + "R_PPC_EMB_NADDR16_HI": true, + "R_PPC_EMB_NADDR16_LO": true, + "R_PPC_EMB_NADDR32": true, + "R_PPC_EMB_RELSDA": true, + "R_PPC_EMB_RELSEC16": true, + "R_PPC_EMB_RELST_HA": true, + "R_PPC_EMB_RELST_HI": true, + "R_PPC_EMB_RELST_LO": true, + "R_PPC_EMB_SDA21": true, + "R_PPC_EMB_SDA2I16": true, + "R_PPC_EMB_SDA2REL": true, + "R_PPC_EMB_SDAI16": true, + "R_PPC_GLOB_DAT": true, + "R_PPC_GOT16": true, + "R_PPC_GOT16_HA": true, + "R_PPC_GOT16_HI": true, + "R_PPC_GOT16_LO": true, + "R_PPC_GOT_TLSGD16": true, + "R_PPC_GOT_TLSGD16_HA": true, + "R_PPC_GOT_TLSGD16_HI": true, + "R_PPC_GOT_TLSGD16_LO": true, + "R_PPC_GOT_TLSLD16": true, + "R_PPC_GOT_TLSLD16_HA": true, + "R_PPC_GOT_TLSLD16_HI": true, + "R_PPC_GOT_TLSLD16_LO": true, + "R_PPC_GOT_TPREL16": true, + "R_PPC_GOT_TPREL16_HA": true, + "R_PPC_GOT_TPREL16_HI": true, + "R_PPC_GOT_TPREL16_LO": true, + "R_PPC_JMP_SLOT": true, + "R_PPC_LOCAL24PC": true, + "R_PPC_NONE": true, + "R_PPC_PLT16_HA": true, + "R_PPC_PLT16_HI": true, + "R_PPC_PLT16_LO": true, + "R_PPC_PLT32": true, + "R_PPC_PLTREL24": true, + "R_PPC_PLTREL32": true, + "R_PPC_REL14": true, + "R_PPC_REL14_BRNTAKEN": true, + "R_PPC_REL14_BRTAKEN": true, + "R_PPC_REL24": true, + "R_PPC_REL32": true, + "R_PPC_RELATIVE": true, + "R_PPC_SDAREL16": true, + "R_PPC_SECTOFF": true, + "R_PPC_SECTOFF_HA": true, + "R_PPC_SECTOFF_HI": true, + "R_PPC_SECTOFF_LO": true, + "R_PPC_TLS": true, + "R_PPC_TPREL16": true, + "R_PPC_TPREL16_HA": true, + "R_PPC_TPREL16_HI": true, + "R_PPC_TPREL16_LO": true, + "R_PPC_TPREL32": true, + "R_PPC_UADDR16": true, + "R_PPC_UADDR32": true, + "R_RISCV": true, + "R_RISCV_32": true, + "R_RISCV_32_PCREL": true, + "R_RISCV_64": true, + "R_RISCV_ADD16": true, + "R_RISCV_ADD32": true, + "R_RISCV_ADD64": true, + "R_RISCV_ADD8": true, + "R_RISCV_ALIGN": true, + "R_RISCV_BRANCH": true, + "R_RISCV_CALL": true, + "R_RISCV_CALL_PLT": true, + "R_RISCV_COPY": true, + "R_RISCV_GNU_VTENTRY": true, + "R_RISCV_GNU_VTINHERIT": true, + "R_RISCV_GOT_HI20": true, + "R_RISCV_GPREL_I": true, + "R_RISCV_GPREL_S": true, + "R_RISCV_HI20": true, + "R_RISCV_JAL": true, + "R_RISCV_JUMP_SLOT": true, + "R_RISCV_LO12_I": true, + "R_RISCV_LO12_S": true, + "R_RISCV_NONE": true, + "R_RISCV_PCREL_HI20": true, + "R_RISCV_PCREL_LO12_I": true, + "R_RISCV_PCREL_LO12_S": true, + "R_RISCV_RELATIVE": true, + "R_RISCV_RELAX": true, + "R_RISCV_RVC_BRANCH": true, + "R_RISCV_RVC_JUMP": true, + "R_RISCV_RVC_LUI": true, + "R_RISCV_SET16": true, + "R_RISCV_SET32": true, + "R_RISCV_SET6": true, + "R_RISCV_SET8": true, + "R_RISCV_SUB16": true, + "R_RISCV_SUB32": true, + "R_RISCV_SUB6": true, + "R_RISCV_SUB64": true, + "R_RISCV_SUB8": true, + "R_RISCV_TLS_DTPMOD32": true, + "R_RISCV_TLS_DTPMOD64": true, + "R_RISCV_TLS_DTPREL32": true, + "R_RISCV_TLS_DTPREL64": true, + "R_RISCV_TLS_GD_HI20": true, + "R_RISCV_TLS_GOT_HI20": true, + "R_RISCV_TLS_TPREL32": true, + "R_RISCV_TLS_TPREL64": true, + "R_RISCV_TPREL_ADD": true, + "R_RISCV_TPREL_HI20": true, + "R_RISCV_TPREL_I": true, + "R_RISCV_TPREL_LO12_I": true, + "R_RISCV_TPREL_LO12_S": true, + "R_RISCV_TPREL_S": true, + "R_SPARC": true, + "R_SPARC_10": true, + "R_SPARC_11": true, + "R_SPARC_13": true, + "R_SPARC_16": true, + "R_SPARC_22": true, + "R_SPARC_32": true, + "R_SPARC_5": true, + "R_SPARC_6": true, + "R_SPARC_64": true, + "R_SPARC_7": true, + "R_SPARC_8": true, + "R_SPARC_COPY": true, + "R_SPARC_DISP16": true, + "R_SPARC_DISP32": true, + "R_SPARC_DISP64": true, + "R_SPARC_DISP8": true, + "R_SPARC_GLOB_DAT": true, + "R_SPARC_GLOB_JMP": true, + "R_SPARC_GOT10": true, + "R_SPARC_GOT13": true, + "R_SPARC_GOT22": true, + "R_SPARC_H44": true, + "R_SPARC_HH22": true, + "R_SPARC_HI22": true, + "R_SPARC_HIPLT22": true, + "R_SPARC_HIX22": true, + "R_SPARC_HM10": true, + "R_SPARC_JMP_SLOT": true, + "R_SPARC_L44": true, + "R_SPARC_LM22": true, + "R_SPARC_LO10": true, + "R_SPARC_LOPLT10": true, + "R_SPARC_LOX10": true, + "R_SPARC_M44": true, + "R_SPARC_NONE": true, + "R_SPARC_OLO10": true, + "R_SPARC_PC10": true, + "R_SPARC_PC22": true, + "R_SPARC_PCPLT10": true, + "R_SPARC_PCPLT22": true, + "R_SPARC_PCPLT32": true, + "R_SPARC_PC_HH22": true, + "R_SPARC_PC_HM10": true, + "R_SPARC_PC_LM22": true, + "R_SPARC_PLT32": true, + "R_SPARC_PLT64": true, + "R_SPARC_REGISTER": true, + "R_SPARC_RELATIVE": true, + "R_SPARC_UA16": true, + "R_SPARC_UA32": true, + "R_SPARC_UA64": true, + "R_SPARC_WDISP16": true, + "R_SPARC_WDISP19": true, + "R_SPARC_WDISP22": true, + "R_SPARC_WDISP30": true, + "R_SPARC_WPLT30": true, + "R_SYM32": true, + "R_SYM64": true, + "R_TYPE32": true, + "R_TYPE64": true, + "R_X86_64": true, + "R_X86_64_16": true, + "R_X86_64_32": true, + "R_X86_64_32S": true, + "R_X86_64_64": true, + "R_X86_64_8": true, + "R_X86_64_COPY": true, + "R_X86_64_DTPMOD64": true, + "R_X86_64_DTPOFF32": true, + "R_X86_64_DTPOFF64": true, + "R_X86_64_GLOB_DAT": true, + "R_X86_64_GOT32": true, + "R_X86_64_GOT64": true, + "R_X86_64_GOTOFF64": true, + "R_X86_64_GOTPC32": true, + "R_X86_64_GOTPC32_TLSDESC": true, + "R_X86_64_GOTPC64": true, + "R_X86_64_GOTPCREL": true, + "R_X86_64_GOTPCREL64": true, + "R_X86_64_GOTPCRELX": true, + "R_X86_64_GOTPLT64": true, + "R_X86_64_GOTTPOFF": true, + "R_X86_64_IRELATIVE": true, + "R_X86_64_JMP_SLOT": true, + "R_X86_64_NONE": true, + "R_X86_64_PC16": true, + "R_X86_64_PC32": true, + "R_X86_64_PC32_BND": true, + "R_X86_64_PC64": true, + "R_X86_64_PC8": true, + "R_X86_64_PLT32": true, + "R_X86_64_PLT32_BND": true, + "R_X86_64_PLTOFF64": true, + "R_X86_64_RELATIVE": true, + "R_X86_64_RELATIVE64": true, + "R_X86_64_REX_GOTPCRELX": true, + "R_X86_64_SIZE32": true, + "R_X86_64_SIZE64": true, + "R_X86_64_TLSDESC": true, + "R_X86_64_TLSDESC_CALL": true, + "R_X86_64_TLSGD": true, + "R_X86_64_TLSLD": true, + "R_X86_64_TPOFF32": true, + "R_X86_64_TPOFF64": true, + "Rel32": true, + "Rel64": true, + "Rela32": true, + "Rela64": true, + "SHF_ALLOC": true, + "SHF_COMPRESSED": true, + "SHF_EXECINSTR": true, + "SHF_GROUP": true, + "SHF_INFO_LINK": true, + "SHF_LINK_ORDER": true, + "SHF_MASKOS": true, + "SHF_MASKPROC": true, + "SHF_MERGE": true, + "SHF_OS_NONCONFORMING": true, + "SHF_STRINGS": true, + "SHF_TLS": true, + "SHF_WRITE": true, + "SHN_ABS": true, + "SHN_COMMON": true, + "SHN_HIOS": true, + "SHN_HIPROC": true, + "SHN_HIRESERVE": true, + "SHN_LOOS": true, + "SHN_LOPROC": true, + "SHN_LORESERVE": true, + "SHN_UNDEF": true, + "SHN_XINDEX": true, + "SHT_DYNAMIC": true, + "SHT_DYNSYM": true, + "SHT_FINI_ARRAY": true, + "SHT_GNU_ATTRIBUTES": true, + "SHT_GNU_HASH": true, + "SHT_GNU_LIBLIST": true, + "SHT_GNU_VERDEF": true, + "SHT_GNU_VERNEED": true, + "SHT_GNU_VERSYM": true, + "SHT_GROUP": true, + "SHT_HASH": true, + "SHT_HIOS": true, + "SHT_HIPROC": true, + "SHT_HIUSER": true, + "SHT_INIT_ARRAY": true, + "SHT_LOOS": true, + "SHT_LOPROC": true, + "SHT_LOUSER": true, + "SHT_NOBITS": true, + "SHT_NOTE": true, + "SHT_NULL": true, + "SHT_PREINIT_ARRAY": true, + "SHT_PROGBITS": true, + "SHT_REL": true, + "SHT_RELA": true, + "SHT_SHLIB": true, + "SHT_STRTAB": true, + "SHT_SYMTAB": true, + "SHT_SYMTAB_SHNDX": true, + "STB_GLOBAL": true, + "STB_HIOS": true, + "STB_HIPROC": true, + "STB_LOCAL": true, + "STB_LOOS": true, + "STB_LOPROC": true, + "STB_WEAK": true, + "STT_COMMON": true, + "STT_FILE": true, + "STT_FUNC": true, + "STT_HIOS": true, + "STT_HIPROC": true, + "STT_LOOS": true, + "STT_LOPROC": true, + "STT_NOTYPE": true, + "STT_OBJECT": true, + "STT_SECTION": true, + "STT_TLS": true, + "STV_DEFAULT": true, + "STV_HIDDEN": true, + "STV_INTERNAL": true, + "STV_PROTECTED": true, + "ST_BIND": true, + "ST_INFO": true, + "ST_TYPE": true, + "ST_VISIBILITY": true, + "Section": true, + "Section32": true, + "Section64": true, + "SectionFlag": true, + "SectionHeader": true, + "SectionIndex": true, + "SectionType": true, + "Sym32": true, + "Sym32Size": true, + "Sym64": true, + "Sym64Size": true, + "SymBind": true, + "SymType": true, + "SymVis": true, + "Symbol": true, + "Type": true, + "Version": true, + }, + "debug/gosym": map[string]bool{ + "DecodingError": true, + "Func": true, + "LineTable": true, + "NewLineTable": true, + "NewTable": true, + "Obj": true, + "Sym": true, + "Table": true, + "UnknownFileError": true, + "UnknownLineError": true, + }, + "debug/macho": map[string]bool{ + "ARM64_RELOC_ADDEND": true, + "ARM64_RELOC_BRANCH26": true, + "ARM64_RELOC_GOT_LOAD_PAGE21": true, + "ARM64_RELOC_GOT_LOAD_PAGEOFF12": true, + "ARM64_RELOC_PAGE21": true, + "ARM64_RELOC_PAGEOFF12": true, + "ARM64_RELOC_POINTER_TO_GOT": true, + "ARM64_RELOC_SUBTRACTOR": true, + "ARM64_RELOC_TLVP_LOAD_PAGE21": true, + "ARM64_RELOC_TLVP_LOAD_PAGEOFF12": true, + "ARM64_RELOC_UNSIGNED": true, + "ARM_RELOC_BR24": true, + "ARM_RELOC_HALF": true, + "ARM_RELOC_HALF_SECTDIFF": true, + "ARM_RELOC_LOCAL_SECTDIFF": true, + "ARM_RELOC_PAIR": true, + "ARM_RELOC_PB_LA_PTR": true, + "ARM_RELOC_SECTDIFF": true, + "ARM_RELOC_VANILLA": true, + "ARM_THUMB_32BIT_BRANCH": true, + "ARM_THUMB_RELOC_BR22": true, + "Cpu": true, + "Cpu386": true, + "CpuAmd64": true, + "CpuArm": true, + "CpuArm64": true, + "CpuPpc": true, + "CpuPpc64": true, + "Dylib": true, + "DylibCmd": true, + "Dysymtab": true, + "DysymtabCmd": true, + "ErrNotFat": true, + "FatArch": true, + "FatArchHeader": true, + "FatFile": true, + "File": true, + "FileHeader": true, + "FlagAllModsBound": true, + "FlagAllowStackExecution": true, + "FlagAppExtensionSafe": true, + "FlagBindAtLoad": true, + "FlagBindsToWeak": true, + "FlagCanonical": true, + "FlagDeadStrippableDylib": true, + "FlagDyldLink": true, + "FlagForceFlat": true, + "FlagHasTLVDescriptors": true, + "FlagIncrLink": true, + "FlagLazyInit": true, + "FlagNoFixPrebinding": true, + "FlagNoHeapExecution": true, + "FlagNoMultiDefs": true, + "FlagNoReexportedDylibs": true, + "FlagNoUndefs": true, + "FlagPIE": true, + "FlagPrebindable": true, + "FlagPrebound": true, + "FlagRootSafe": true, + "FlagSetuidSafe": true, + "FlagSplitSegs": true, + "FlagSubsectionsViaSymbols": true, + "FlagTwoLevel": true, + "FlagWeakDefines": true, + "FormatError": true, + "GENERIC_RELOC_LOCAL_SECTDIFF": true, + "GENERIC_RELOC_PAIR": true, + "GENERIC_RELOC_PB_LA_PTR": true, + "GENERIC_RELOC_SECTDIFF": true, + "GENERIC_RELOC_TLV": true, + "GENERIC_RELOC_VANILLA": true, + "Load": true, + "LoadBytes": true, + "LoadCmd": true, + "LoadCmdDylib": true, + "LoadCmdDylinker": true, + "LoadCmdDysymtab": true, + "LoadCmdRpath": true, + "LoadCmdSegment": true, + "LoadCmdSegment64": true, + "LoadCmdSymtab": true, + "LoadCmdThread": true, + "LoadCmdUnixThread": true, + "Magic32": true, + "Magic64": true, + "MagicFat": true, + "NewFatFile": true, + "NewFile": true, + "Nlist32": true, + "Nlist64": true, + "Open": true, + "OpenFat": true, + "Regs386": true, + "RegsAMD64": true, + "Reloc": true, + "RelocTypeARM": true, + "RelocTypeARM64": true, + "RelocTypeGeneric": true, + "RelocTypeX86_64": true, + "Rpath": true, + "RpathCmd": true, + "Section": true, + "Section32": true, + "Section64": true, + "SectionHeader": true, + "Segment": true, + "Segment32": true, + "Segment64": true, + "SegmentHeader": true, + "Symbol": true, + "Symtab": true, + "SymtabCmd": true, + "Thread": true, + "Type": true, + "TypeBundle": true, + "TypeDylib": true, + "TypeExec": true, + "TypeObj": true, + "X86_64_RELOC_BRANCH": true, + "X86_64_RELOC_GOT": true, + "X86_64_RELOC_GOT_LOAD": true, + "X86_64_RELOC_SIGNED": true, + "X86_64_RELOC_SIGNED_1": true, + "X86_64_RELOC_SIGNED_2": true, + "X86_64_RELOC_SIGNED_4": true, + "X86_64_RELOC_SUBTRACTOR": true, + "X86_64_RELOC_TLV": true, + "X86_64_RELOC_UNSIGNED": true, + }, + "debug/pe": map[string]bool{ + "COFFSymbol": true, + "COFFSymbolSize": true, + "DataDirectory": true, + "File": true, + "FileHeader": true, + "FormatError": true, + "IMAGE_DIRECTORY_ENTRY_ARCHITECTURE": true, + "IMAGE_DIRECTORY_ENTRY_BASERELOC": true, + "IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT": true, + "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR": true, + "IMAGE_DIRECTORY_ENTRY_DEBUG": true, + "IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT": true, + "IMAGE_DIRECTORY_ENTRY_EXCEPTION": true, + "IMAGE_DIRECTORY_ENTRY_EXPORT": true, + "IMAGE_DIRECTORY_ENTRY_GLOBALPTR": true, + "IMAGE_DIRECTORY_ENTRY_IAT": true, + "IMAGE_DIRECTORY_ENTRY_IMPORT": true, + "IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG": true, + "IMAGE_DIRECTORY_ENTRY_RESOURCE": true, + "IMAGE_DIRECTORY_ENTRY_SECURITY": true, + "IMAGE_DIRECTORY_ENTRY_TLS": true, + "IMAGE_FILE_MACHINE_AM33": true, + "IMAGE_FILE_MACHINE_AMD64": true, + "IMAGE_FILE_MACHINE_ARM": true, + "IMAGE_FILE_MACHINE_ARM64": true, + "IMAGE_FILE_MACHINE_ARMNT": true, + "IMAGE_FILE_MACHINE_EBC": true, + "IMAGE_FILE_MACHINE_I386": true, + "IMAGE_FILE_MACHINE_IA64": true, + "IMAGE_FILE_MACHINE_M32R": true, + "IMAGE_FILE_MACHINE_MIPS16": true, + "IMAGE_FILE_MACHINE_MIPSFPU": true, + "IMAGE_FILE_MACHINE_MIPSFPU16": true, + "IMAGE_FILE_MACHINE_POWERPC": true, + "IMAGE_FILE_MACHINE_POWERPCFP": true, + "IMAGE_FILE_MACHINE_R4000": true, + "IMAGE_FILE_MACHINE_SH3": true, + "IMAGE_FILE_MACHINE_SH3DSP": true, + "IMAGE_FILE_MACHINE_SH4": true, + "IMAGE_FILE_MACHINE_SH5": true, + "IMAGE_FILE_MACHINE_THUMB": true, + "IMAGE_FILE_MACHINE_UNKNOWN": true, + "IMAGE_FILE_MACHINE_WCEMIPSV2": true, + "ImportDirectory": true, + "NewFile": true, + "Open": true, + "OptionalHeader32": true, + "OptionalHeader64": true, + "Reloc": true, + "Section": true, + "SectionHeader": true, + "SectionHeader32": true, + "StringTable": true, + "Symbol": true, + }, + "debug/plan9obj": map[string]bool{ + "File": true, + "FileHeader": true, + "Magic386": true, + "Magic64": true, + "MagicAMD64": true, + "MagicARM": true, + "NewFile": true, + "Open": true, + "Section": true, + "SectionHeader": true, + "Sym": true, + }, + "encoding": map[string]bool{ + "BinaryMarshaler": true, + "BinaryUnmarshaler": true, + "TextMarshaler": true, + "TextUnmarshaler": true, + }, + "encoding/ascii85": map[string]bool{ + "CorruptInputError": true, + "Decode": true, + "Encode": true, + "MaxEncodedLen": true, + "NewDecoder": true, + "NewEncoder": true, + }, + "encoding/asn1": map[string]bool{ + "BitString": true, + "ClassApplication": true, + "ClassContextSpecific": true, + "ClassPrivate": true, + "ClassUniversal": true, + "Enumerated": true, + "Flag": true, + "Marshal": true, + "MarshalWithParams": true, + "NullBytes": true, + "NullRawValue": true, + "ObjectIdentifier": true, + "RawContent": true, + "RawValue": true, + "StructuralError": true, + "SyntaxError": true, + "TagBitString": true, + "TagBoolean": true, + "TagEnum": true, + "TagGeneralString": true, + "TagGeneralizedTime": true, + "TagIA5String": true, + "TagInteger": true, + "TagNull": true, + "TagNumericString": true, + "TagOID": true, + "TagOctetString": true, + "TagPrintableString": true, + "TagSequence": true, + "TagSet": true, + "TagT61String": true, + "TagUTCTime": true, + "TagUTF8String": true, + "Unmarshal": true, + "UnmarshalWithParams": true, + }, + "encoding/base32": map[string]bool{ + "CorruptInputError": true, + "Encoding": true, + "HexEncoding": true, + "NewDecoder": true, + "NewEncoder": true, + "NewEncoding": true, + "NoPadding": true, + "StdEncoding": true, + "StdPadding": true, + }, + "encoding/base64": map[string]bool{ + "CorruptInputError": true, + "Encoding": true, + "NewDecoder": true, + "NewEncoder": true, + "NewEncoding": true, + "NoPadding": true, + "RawStdEncoding": true, + "RawURLEncoding": true, + "StdEncoding": true, + "StdPadding": true, + "URLEncoding": true, + }, + "encoding/binary": map[string]bool{ + "BigEndian": true, + "ByteOrder": true, + "LittleEndian": true, + "MaxVarintLen16": true, + "MaxVarintLen32": true, + "MaxVarintLen64": true, + "PutUvarint": true, + "PutVarint": true, + "Read": true, + "ReadUvarint": true, + "ReadVarint": true, + "Size": true, + "Uvarint": true, + "Varint": true, + "Write": true, + }, + "encoding/csv": map[string]bool{ + "ErrBareQuote": true, + "ErrFieldCount": true, + "ErrQuote": true, + "ErrTrailingComma": true, + "NewReader": true, + "NewWriter": true, + "ParseError": true, + "Reader": true, + "Writer": true, + }, + "encoding/gob": map[string]bool{ + "CommonType": true, + "Decoder": true, + "Encoder": true, + "GobDecoder": true, + "GobEncoder": true, + "NewDecoder": true, + "NewEncoder": true, + "Register": true, + "RegisterName": true, + }, + "encoding/hex": map[string]bool{ + "Decode": true, + "DecodeString": true, + "DecodedLen": true, + "Dump": true, + "Dumper": true, + "Encode": true, + "EncodeToString": true, + "EncodedLen": true, + "ErrLength": true, + "InvalidByteError": true, + "NewDecoder": true, + "NewEncoder": true, + }, + "encoding/json": map[string]bool{ + "Compact": true, + "Decoder": true, + "Delim": true, + "Encoder": true, + "HTMLEscape": true, + "Indent": true, + "InvalidUTF8Error": true, + "InvalidUnmarshalError": true, + "Marshal": true, + "MarshalIndent": true, + "Marshaler": true, + "MarshalerError": true, + "NewDecoder": true, + "NewEncoder": true, + "Number": true, + "RawMessage": true, + "SyntaxError": true, + "Token": true, + "Unmarshal": true, + "UnmarshalFieldError": true, + "UnmarshalTypeError": true, + "Unmarshaler": true, + "UnsupportedTypeError": true, + "UnsupportedValueError": true, + "Valid": true, + }, + "encoding/pem": map[string]bool{ + "Block": true, + "Decode": true, + "Encode": true, + "EncodeToMemory": true, + }, + "encoding/xml": map[string]bool{ + "Attr": true, + "CharData": true, + "Comment": true, + "CopyToken": true, + "Decoder": true, + "Directive": true, + "Encoder": true, + "EndElement": true, + "Escape": true, + "EscapeText": true, + "HTMLAutoClose": true, + "HTMLEntity": true, + "Header": true, + "Marshal": true, + "MarshalIndent": true, + "Marshaler": true, + "MarshalerAttr": true, + "Name": true, + "NewDecoder": true, + "NewEncoder": true, + "NewTokenDecoder": true, + "ProcInst": true, + "StartElement": true, + "SyntaxError": true, + "TagPathError": true, + "Token": true, + "TokenReader": true, + "Unmarshal": true, + "UnmarshalError": true, + "Unmarshaler": true, + "UnmarshalerAttr": true, + "UnsupportedTypeError": true, + }, + "errors": map[string]bool{ + "New": true, + }, + "expvar": map[string]bool{ + "Do": true, + "Float": true, + "Func": true, + "Get": true, + "Handler": true, + "Int": true, + "KeyValue": true, + "Map": true, + "NewFloat": true, + "NewInt": true, + "NewMap": true, + "NewString": true, + "Publish": true, + "String": true, + "Var": true, + }, + "flag": map[string]bool{ + "Arg": true, + "Args": true, + "Bool": true, + "BoolVar": true, + "CommandLine": true, + "ContinueOnError": true, + "Duration": true, + "DurationVar": true, + "ErrHelp": true, + "ErrorHandling": true, + "ExitOnError": true, + "Flag": true, + "FlagSet": true, + "Float64": true, + "Float64Var": true, + "Getter": true, + "Int": true, + "Int64": true, + "Int64Var": true, + "IntVar": true, + "Lookup": true, + "NArg": true, + "NFlag": true, + "NewFlagSet": true, + "PanicOnError": true, + "Parse": true, + "Parsed": true, + "PrintDefaults": true, + "Set": true, + "String": true, + "StringVar": true, + "Uint": true, + "Uint64": true, + "Uint64Var": true, + "UintVar": true, + "UnquoteUsage": true, + "Usage": true, + "Value": true, + "Var": true, + "Visit": true, + "VisitAll": true, + }, + "fmt": map[string]bool{ + "Errorf": true, + "Formatter": true, + "Fprint": true, + "Fprintf": true, + "Fprintln": true, + "Fscan": true, + "Fscanf": true, + "Fscanln": true, + "GoStringer": true, + "Print": true, + "Printf": true, + "Println": true, + "Scan": true, + "ScanState": true, + "Scanf": true, + "Scanln": true, + "Scanner": true, + "Sprint": true, + "Sprintf": true, + "Sprintln": true, + "Sscan": true, + "Sscanf": true, + "Sscanln": true, + "State": true, + "Stringer": true, + }, + "go/ast": map[string]bool{ + "ArrayType": true, + "AssignStmt": true, + "Bad": true, + "BadDecl": true, + "BadExpr": true, + "BadStmt": true, + "BasicLit": true, + "BinaryExpr": true, + "BlockStmt": true, + "BranchStmt": true, + "CallExpr": true, + "CaseClause": true, + "ChanDir": true, + "ChanType": true, + "CommClause": true, + "Comment": true, + "CommentGroup": true, + "CommentMap": true, + "CompositeLit": true, + "Con": true, + "DeclStmt": true, + "DeferStmt": true, + "Ellipsis": true, + "EmptyStmt": true, + "ExprStmt": true, + "Field": true, + "FieldFilter": true, + "FieldList": true, + "File": true, + "FileExports": true, + "Filter": true, + "FilterDecl": true, + "FilterFile": true, + "FilterFuncDuplicates": true, + "FilterImportDuplicates": true, + "FilterPackage": true, + "FilterUnassociatedComments": true, + "ForStmt": true, + "Fprint": true, + "Fun": true, + "FuncDecl": true, + "FuncLit": true, + "FuncType": true, + "GenDecl": true, + "GoStmt": true, + "Ident": true, + "IfStmt": true, + "ImportSpec": true, + "Importer": true, + "IncDecStmt": true, + "IndexExpr": true, + "Inspect": true, + "InterfaceType": true, + "IsExported": true, + "KeyValueExpr": true, + "LabeledStmt": true, + "Lbl": true, + "MapType": true, + "MergeMode": true, + "MergePackageFiles": true, + "NewCommentMap": true, + "NewIdent": true, + "NewObj": true, + "NewPackage": true, + "NewScope": true, + "Node": true, + "NotNilFilter": true, + "ObjKind": true, + "Object": true, + "Package": true, + "PackageExports": true, + "ParenExpr": true, + "Pkg": true, + "Print": true, + "RECV": true, + "RangeStmt": true, + "ReturnStmt": true, + "SEND": true, + "Scope": true, + "SelectStmt": true, + "SelectorExpr": true, + "SendStmt": true, + "SliceExpr": true, + "SortImports": true, + "StarExpr": true, + "StructType": true, + "SwitchStmt": true, + "Typ": true, + "TypeAssertExpr": true, + "TypeSpec": true, + "TypeSwitchStmt": true, + "UnaryExpr": true, + "ValueSpec": true, + "Var": true, + "Visitor": true, + "Walk": true, + }, + "go/build": map[string]bool{ + "AllowBinary": true, + "ArchChar": true, + "Context": true, + "Default": true, + "FindOnly": true, + "IgnoreVendor": true, + "Import": true, + "ImportComment": true, + "ImportDir": true, + "ImportMode": true, + "IsLocalImport": true, + "MultiplePackageError": true, + "NoGoError": true, + "Package": true, + "ToolDir": true, + }, + "go/constant": map[string]bool{ + "BinaryOp": true, + "BitLen": true, + "Bool": true, + "BoolVal": true, + "Bytes": true, + "Compare": true, + "Complex": true, + "Denom": true, + "Float": true, + "Float32Val": true, + "Float64Val": true, + "Imag": true, + "Int": true, + "Int64Val": true, + "Kind": true, + "MakeBool": true, + "MakeFloat64": true, + "MakeFromBytes": true, + "MakeFromLiteral": true, + "MakeImag": true, + "MakeInt64": true, + "MakeString": true, + "MakeUint64": true, + "MakeUnknown": true, + "Num": true, + "Real": true, + "Shift": true, + "Sign": true, + "String": true, + "StringVal": true, + "ToComplex": true, + "ToFloat": true, + "ToInt": true, + "Uint64Val": true, + "UnaryOp": true, + "Unknown": true, + }, + "go/doc": map[string]bool{ + "AllDecls": true, + "AllMethods": true, + "Example": true, + "Examples": true, + "Filter": true, + "Func": true, + "IllegalPrefixes": true, + "IsPredeclared": true, + "Mode": true, + "New": true, + "Note": true, + "Package": true, + "PreserveAST": true, + "Synopsis": true, + "ToHTML": true, + "ToText": true, + "Type": true, + "Value": true, + }, + "go/format": map[string]bool{ + "Node": true, + "Source": true, + }, + "go/importer": map[string]bool{ + "Default": true, + "For": true, + "ForCompiler": true, + "Lookup": true, + }, + "go/parser": map[string]bool{ + "AllErrors": true, + "DeclarationErrors": true, + "ImportsOnly": true, + "Mode": true, + "PackageClauseOnly": true, + "ParseComments": true, + "ParseDir": true, + "ParseExpr": true, + "ParseExprFrom": true, + "ParseFile": true, + "SpuriousErrors": true, + "Trace": true, + }, + "go/printer": map[string]bool{ + "CommentedNode": true, + "Config": true, + "Fprint": true, + "Mode": true, + "RawFormat": true, + "SourcePos": true, + "TabIndent": true, + "UseSpaces": true, + }, + "go/scanner": map[string]bool{ + "Error": true, + "ErrorHandler": true, + "ErrorList": true, + "Mode": true, + "PrintError": true, + "ScanComments": true, + "Scanner": true, + }, + "go/token": map[string]bool{ + "ADD": true, + "ADD_ASSIGN": true, + "AND": true, + "AND_ASSIGN": true, + "AND_NOT": true, + "AND_NOT_ASSIGN": true, + "ARROW": true, + "ASSIGN": true, + "BREAK": true, + "CASE": true, + "CHAN": true, + "CHAR": true, + "COLON": true, + "COMMA": true, + "COMMENT": true, + "CONST": true, + "CONTINUE": true, + "DEC": true, + "DEFAULT": true, + "DEFER": true, + "DEFINE": true, + "ELLIPSIS": true, + "ELSE": true, + "EOF": true, + "EQL": true, + "FALLTHROUGH": true, + "FLOAT": true, + "FOR": true, + "FUNC": true, + "File": true, + "FileSet": true, + "GEQ": true, + "GO": true, + "GOTO": true, + "GTR": true, + "HighestPrec": true, + "IDENT": true, + "IF": true, + "ILLEGAL": true, + "IMAG": true, + "IMPORT": true, + "INC": true, + "INT": true, + "INTERFACE": true, + "LAND": true, + "LBRACE": true, + "LBRACK": true, + "LEQ": true, + "LOR": true, + "LPAREN": true, + "LSS": true, + "Lookup": true, + "LowestPrec": true, + "MAP": true, + "MUL": true, + "MUL_ASSIGN": true, + "NEQ": true, + "NOT": true, + "NewFileSet": true, + "NoPos": true, + "OR": true, + "OR_ASSIGN": true, + "PACKAGE": true, + "PERIOD": true, + "Pos": true, + "Position": true, + "QUO": true, + "QUO_ASSIGN": true, + "RANGE": true, + "RBRACE": true, + "RBRACK": true, + "REM": true, + "REM_ASSIGN": true, + "RETURN": true, + "RPAREN": true, + "SELECT": true, + "SEMICOLON": true, + "SHL": true, + "SHL_ASSIGN": true, + "SHR": true, + "SHR_ASSIGN": true, + "STRING": true, + "STRUCT": true, + "SUB": true, + "SUB_ASSIGN": true, + "SWITCH": true, + "TYPE": true, + "Token": true, + "UnaryPrec": true, + "VAR": true, + "XOR": true, + "XOR_ASSIGN": true, + }, + "go/types": map[string]bool{ + "Array": true, + "AssertableTo": true, + "AssignableTo": true, + "Basic": true, + "BasicInfo": true, + "BasicKind": true, + "Bool": true, + "Builtin": true, + "Byte": true, + "Chan": true, + "ChanDir": true, + "Checker": true, + "Comparable": true, + "Complex128": true, + "Complex64": true, + "Config": true, + "Const": true, + "ConvertibleTo": true, + "DefPredeclaredTestFuncs": true, + "Default": true, + "Error": true, + "Eval": true, + "ExprString": true, + "FieldVal": true, + "Float32": true, + "Float64": true, + "Func": true, + "Id": true, + "Identical": true, + "IdenticalIgnoreTags": true, + "Implements": true, + "ImportMode": true, + "Importer": true, + "ImporterFrom": true, + "Info": true, + "Initializer": true, + "Int": true, + "Int16": true, + "Int32": true, + "Int64": true, + "Int8": true, + "Interface": true, + "Invalid": true, + "IsBoolean": true, + "IsComplex": true, + "IsConstType": true, + "IsFloat": true, + "IsInteger": true, + "IsInterface": true, + "IsNumeric": true, + "IsOrdered": true, + "IsString": true, + "IsUnsigned": true, + "IsUntyped": true, + "Label": true, + "LookupFieldOrMethod": true, + "Map": true, + "MethodExpr": true, + "MethodSet": true, + "MethodVal": true, + "MissingMethod": true, + "Named": true, + "NewArray": true, + "NewChan": true, + "NewChecker": true, + "NewConst": true, + "NewField": true, + "NewFunc": true, + "NewInterface": true, + "NewInterfaceType": true, + "NewLabel": true, + "NewMap": true, + "NewMethodSet": true, + "NewNamed": true, + "NewPackage": true, + "NewParam": true, + "NewPkgName": true, + "NewPointer": true, + "NewScope": true, + "NewSignature": true, + "NewSlice": true, + "NewStruct": true, + "NewTuple": true, + "NewTypeName": true, + "NewVar": true, + "Nil": true, + "ObjectString": true, + "Package": true, + "PkgName": true, + "Pointer": true, + "Qualifier": true, + "RecvOnly": true, + "RelativeTo": true, + "Rune": true, + "Scope": true, + "Selection": true, + "SelectionKind": true, + "SelectionString": true, + "SendOnly": true, + "SendRecv": true, + "Signature": true, + "Sizes": true, + "SizesFor": true, + "Slice": true, + "StdSizes": true, + "String": true, + "Struct": true, + "Tuple": true, + "Typ": true, + "Type": true, + "TypeAndValue": true, + "TypeName": true, + "TypeString": true, + "Uint": true, + "Uint16": true, + "Uint32": true, + "Uint64": true, + "Uint8": true, + "Uintptr": true, + "Universe": true, + "Unsafe": true, + "UnsafePointer": true, + "UntypedBool": true, + "UntypedComplex": true, + "UntypedFloat": true, + "UntypedInt": true, + "UntypedNil": true, + "UntypedRune": true, + "UntypedString": true, + "Var": true, + "WriteExpr": true, + "WriteSignature": true, + "WriteType": true, + }, + "hash": map[string]bool{ + "Hash": true, + "Hash32": true, + "Hash64": true, + }, + "hash/adler32": map[string]bool{ + "Checksum": true, + "New": true, + "Size": true, + }, + "hash/crc32": map[string]bool{ + "Castagnoli": true, + "Checksum": true, + "ChecksumIEEE": true, + "IEEE": true, + "IEEETable": true, + "Koopman": true, + "MakeTable": true, + "New": true, + "NewIEEE": true, + "Size": true, + "Table": true, + "Update": true, + }, + "hash/crc64": map[string]bool{ + "Checksum": true, + "ECMA": true, + "ISO": true, + "MakeTable": true, + "New": true, + "Size": true, + "Table": true, + "Update": true, + }, + "hash/fnv": map[string]bool{ + "New128": true, + "New128a": true, + "New32": true, + "New32a": true, + "New64": true, + "New64a": true, + }, + "html": map[string]bool{ + "EscapeString": true, + "UnescapeString": true, + }, + "html/template": map[string]bool{ + "CSS": true, + "ErrAmbigContext": true, + "ErrBadHTML": true, + "ErrBranchEnd": true, + "ErrEndContext": true, + "ErrNoSuchTemplate": true, + "ErrOutputContext": true, + "ErrPartialCharset": true, + "ErrPartialEscape": true, + "ErrPredefinedEscaper": true, + "ErrRangeLoopReentry": true, + "ErrSlashAmbig": true, + "Error": true, + "ErrorCode": true, + "FuncMap": true, + "HTML": true, + "HTMLAttr": true, + "HTMLEscape": true, + "HTMLEscapeString": true, + "HTMLEscaper": true, + "IsTrue": true, + "JS": true, + "JSEscape": true, + "JSEscapeString": true, + "JSEscaper": true, + "JSStr": true, + "Must": true, + "New": true, + "OK": true, + "ParseFiles": true, + "ParseGlob": true, + "Srcset": true, + "Template": true, + "URL": true, + "URLQueryEscaper": true, + }, + "image": map[string]bool{ + "Alpha": true, + "Alpha16": true, + "Black": true, + "CMYK": true, + "Config": true, + "Decode": true, + "DecodeConfig": true, + "ErrFormat": true, + "Gray": true, + "Gray16": true, + "Image": true, + "NRGBA": true, + "NRGBA64": true, + "NYCbCrA": true, + "NewAlpha": true, + "NewAlpha16": true, + "NewCMYK": true, + "NewGray": true, + "NewGray16": true, + "NewNRGBA": true, + "NewNRGBA64": true, + "NewNYCbCrA": true, + "NewPaletted": true, + "NewRGBA": true, + "NewRGBA64": true, + "NewUniform": true, + "NewYCbCr": true, + "Opaque": true, + "Paletted": true, + "PalettedImage": true, + "Point": true, + "Pt": true, + "RGBA": true, + "RGBA64": true, + "Rect": true, + "Rectangle": true, + "RegisterFormat": true, + "Transparent": true, + "Uniform": true, + "White": true, + "YCbCr": true, + "YCbCrSubsampleRatio": true, + "YCbCrSubsampleRatio410": true, + "YCbCrSubsampleRatio411": true, + "YCbCrSubsampleRatio420": true, + "YCbCrSubsampleRatio422": true, + "YCbCrSubsampleRatio440": true, + "YCbCrSubsampleRatio444": true, + "ZP": true, + "ZR": true, + }, + "image/color": map[string]bool{ + "Alpha": true, + "Alpha16": true, + "Alpha16Model": true, + "AlphaModel": true, + "Black": true, + "CMYK": true, + "CMYKModel": true, + "CMYKToRGB": true, + "Color": true, + "Gray": true, + "Gray16": true, + "Gray16Model": true, + "GrayModel": true, + "Model": true, + "ModelFunc": true, + "NRGBA": true, + "NRGBA64": true, + "NRGBA64Model": true, + "NRGBAModel": true, + "NYCbCrA": true, + "NYCbCrAModel": true, + "Opaque": true, + "Palette": true, + "RGBA": true, + "RGBA64": true, + "RGBA64Model": true, + "RGBAModel": true, + "RGBToCMYK": true, + "RGBToYCbCr": true, + "Transparent": true, + "White": true, + "YCbCr": true, + "YCbCrModel": true, + "YCbCrToRGB": true, + }, + "image/color/palette": map[string]bool{ + "Plan9": true, + "WebSafe": true, + }, + "image/draw": map[string]bool{ + "Draw": true, + "DrawMask": true, + "Drawer": true, + "FloydSteinberg": true, + "Image": true, + "Op": true, + "Over": true, + "Quantizer": true, + "Src": true, + }, + "image/gif": map[string]bool{ + "Decode": true, + "DecodeAll": true, + "DecodeConfig": true, + "DisposalBackground": true, + "DisposalNone": true, + "DisposalPrevious": true, + "Encode": true, + "EncodeAll": true, + "GIF": true, + "Options": true, + }, + "image/jpeg": map[string]bool{ + "Decode": true, + "DecodeConfig": true, + "DefaultQuality": true, + "Encode": true, + "FormatError": true, + "Options": true, + "Reader": true, + "UnsupportedError": true, + }, + "image/png": map[string]bool{ + "BestCompression": true, + "BestSpeed": true, + "CompressionLevel": true, + "Decode": true, + "DecodeConfig": true, + "DefaultCompression": true, + "Encode": true, + "Encoder": true, + "EncoderBuffer": true, + "EncoderBufferPool": true, + "FormatError": true, + "NoCompression": true, + "UnsupportedError": true, + }, + "index/suffixarray": map[string]bool{ + "Index": true, + "New": true, + }, + "io": map[string]bool{ + "ByteReader": true, + "ByteScanner": true, + "ByteWriter": true, + "Closer": true, + "Copy": true, + "CopyBuffer": true, + "CopyN": true, + "EOF": true, + "ErrClosedPipe": true, + "ErrNoProgress": true, + "ErrShortBuffer": true, + "ErrShortWrite": true, + "ErrUnexpectedEOF": true, + "LimitReader": true, + "LimitedReader": true, + "MultiReader": true, + "MultiWriter": true, + "NewSectionReader": true, + "Pipe": true, + "PipeReader": true, + "PipeWriter": true, + "ReadAtLeast": true, + "ReadCloser": true, + "ReadFull": true, + "ReadSeeker": true, + "ReadWriteCloser": true, + "ReadWriteSeeker": true, + "ReadWriter": true, + "Reader": true, + "ReaderAt": true, + "ReaderFrom": true, + "RuneReader": true, + "RuneScanner": true, + "SectionReader": true, + "SeekCurrent": true, + "SeekEnd": true, + "SeekStart": true, + "Seeker": true, + "StringWriter": true, + "TeeReader": true, + "WriteCloser": true, + "WriteSeeker": true, + "WriteString": true, + "Writer": true, + "WriterAt": true, + "WriterTo": true, + }, + "io/ioutil": map[string]bool{ + "Discard": true, + "NopCloser": true, + "ReadAll": true, + "ReadDir": true, + "ReadFile": true, + "TempDir": true, + "TempFile": true, + "WriteFile": true, + }, + "log": map[string]bool{ + "Fatal": true, + "Fatalf": true, + "Fatalln": true, + "Flags": true, + "LUTC": true, + "Ldate": true, + "Llongfile": true, + "Lmicroseconds": true, + "Logger": true, + "Lshortfile": true, + "LstdFlags": true, + "Ltime": true, + "New": true, + "Output": true, + "Panic": true, + "Panicf": true, + "Panicln": true, + "Prefix": true, + "Print": true, + "Printf": true, + "Println": true, + "SetFlags": true, + "SetOutput": true, + "SetPrefix": true, + }, + "log/syslog": map[string]bool{ + "Dial": true, + "LOG_ALERT": true, + "LOG_AUTH": true, + "LOG_AUTHPRIV": true, + "LOG_CRIT": true, + "LOG_CRON": true, + "LOG_DAEMON": true, + "LOG_DEBUG": true, + "LOG_EMERG": true, + "LOG_ERR": true, + "LOG_FTP": true, + "LOG_INFO": true, + "LOG_KERN": true, + "LOG_LOCAL0": true, + "LOG_LOCAL1": true, + "LOG_LOCAL2": true, + "LOG_LOCAL3": true, + "LOG_LOCAL4": true, + "LOG_LOCAL5": true, + "LOG_LOCAL6": true, + "LOG_LOCAL7": true, + "LOG_LPR": true, + "LOG_MAIL": true, + "LOG_NEWS": true, + "LOG_NOTICE": true, + "LOG_SYSLOG": true, + "LOG_USER": true, + "LOG_UUCP": true, + "LOG_WARNING": true, + "New": true, + "NewLogger": true, + "Priority": true, + "Writer": true, + }, + "math": map[string]bool{ + "Abs": true, + "Acos": true, + "Acosh": true, + "Asin": true, + "Asinh": true, + "Atan": true, + "Atan2": true, + "Atanh": true, + "Cbrt": true, + "Ceil": true, + "Copysign": true, + "Cos": true, + "Cosh": true, + "Dim": true, + "E": true, + "Erf": true, + "Erfc": true, + "Erfcinv": true, + "Erfinv": true, + "Exp": true, + "Exp2": true, + "Expm1": true, + "Float32bits": true, + "Float32frombits": true, + "Float64bits": true, + "Float64frombits": true, + "Floor": true, + "Frexp": true, + "Gamma": true, + "Hypot": true, + "Ilogb": true, + "Inf": true, + "IsInf": true, + "IsNaN": true, + "J0": true, + "J1": true, + "Jn": true, + "Ldexp": true, + "Lgamma": true, + "Ln10": true, + "Ln2": true, + "Log": true, + "Log10": true, + "Log10E": true, + "Log1p": true, + "Log2": true, + "Log2E": true, + "Logb": true, + "Max": true, + "MaxFloat32": true, + "MaxFloat64": true, + "MaxInt16": true, + "MaxInt32": true, + "MaxInt64": true, + "MaxInt8": true, + "MaxUint16": true, + "MaxUint32": true, + "MaxUint64": true, + "MaxUint8": true, + "Min": true, + "MinInt16": true, + "MinInt32": true, + "MinInt64": true, + "MinInt8": true, + "Mod": true, + "Modf": true, + "NaN": true, + "Nextafter": true, + "Nextafter32": true, + "Phi": true, + "Pi": true, + "Pow": true, + "Pow10": true, + "Remainder": true, + "Round": true, + "RoundToEven": true, + "Signbit": true, + "Sin": true, + "Sincos": true, + "Sinh": true, + "SmallestNonzeroFloat32": true, + "SmallestNonzeroFloat64": true, + "Sqrt": true, + "Sqrt2": true, + "SqrtE": true, + "SqrtPhi": true, + "SqrtPi": true, + "Tan": true, + "Tanh": true, + "Trunc": true, + "Y0": true, + "Y1": true, + "Yn": true, + }, + "math/big": map[string]bool{ + "Above": true, + "Accuracy": true, + "AwayFromZero": true, + "Below": true, + "ErrNaN": true, + "Exact": true, + "Float": true, + "Int": true, + "Jacobi": true, + "MaxBase": true, + "MaxExp": true, + "MaxPrec": true, + "MinExp": true, + "NewFloat": true, + "NewInt": true, + "NewRat": true, + "ParseFloat": true, + "Rat": true, + "RoundingMode": true, + "ToNearestAway": true, + "ToNearestEven": true, + "ToNegativeInf": true, + "ToPositiveInf": true, + "ToZero": true, + "Word": true, + }, + "math/bits": map[string]bool{ + "Add": true, + "Add32": true, + "Add64": true, + "Div": true, + "Div32": true, + "Div64": true, + "LeadingZeros": true, + "LeadingZeros16": true, + "LeadingZeros32": true, + "LeadingZeros64": true, + "LeadingZeros8": true, + "Len": true, + "Len16": true, + "Len32": true, + "Len64": true, + "Len8": true, + "Mul": true, + "Mul32": true, + "Mul64": true, + "OnesCount": true, + "OnesCount16": true, + "OnesCount32": true, + "OnesCount64": true, + "OnesCount8": true, + "Reverse": true, + "Reverse16": true, + "Reverse32": true, + "Reverse64": true, + "Reverse8": true, + "ReverseBytes": true, + "ReverseBytes16": true, + "ReverseBytes32": true, + "ReverseBytes64": true, + "RotateLeft": true, + "RotateLeft16": true, + "RotateLeft32": true, + "RotateLeft64": true, + "RotateLeft8": true, + "Sub": true, + "Sub32": true, + "Sub64": true, + "TrailingZeros": true, + "TrailingZeros16": true, + "TrailingZeros32": true, + "TrailingZeros64": true, + "TrailingZeros8": true, + "UintSize": true, + }, + "math/cmplx": map[string]bool{ + "Abs": true, + "Acos": true, + "Acosh": true, + "Asin": true, + "Asinh": true, + "Atan": true, + "Atanh": true, + "Conj": true, + "Cos": true, + "Cosh": true, + "Cot": true, + "Exp": true, + "Inf": true, + "IsInf": true, + "IsNaN": true, + "Log": true, + "Log10": true, + "NaN": true, + "Phase": true, + "Polar": true, + "Pow": true, + "Rect": true, + "Sin": true, + "Sinh": true, + "Sqrt": true, + "Tan": true, + "Tanh": true, + }, + "math/rand": map[string]bool{ + "ExpFloat64": true, + "Float32": true, + "Float64": true, + "Int": true, + "Int31": true, + "Int31n": true, + "Int63": true, + "Int63n": true, + "Intn": true, + "New": true, + "NewSource": true, + "NewZipf": true, + "NormFloat64": true, + "Perm": true, + "Rand": true, + "Read": true, + "Seed": true, + "Shuffle": true, + "Source": true, + "Source64": true, + "Uint32": true, + "Uint64": true, + "Zipf": true, + }, + "mime": map[string]bool{ + "AddExtensionType": true, + "BEncoding": true, + "ErrInvalidMediaParameter": true, + "ExtensionsByType": true, + "FormatMediaType": true, + "ParseMediaType": true, + "QEncoding": true, + "TypeByExtension": true, + "WordDecoder": true, + "WordEncoder": true, + }, + "mime/multipart": map[string]bool{ + "ErrMessageTooLarge": true, + "File": true, + "FileHeader": true, + "Form": true, + "NewReader": true, + "NewWriter": true, + "Part": true, + "Reader": true, + "Writer": true, + }, + "mime/quotedprintable": map[string]bool{ + "NewReader": true, + "NewWriter": true, + "Reader": true, + "Writer": true, + }, + "net": map[string]bool{ + "Addr": true, + "AddrError": true, + "Buffers": true, + "CIDRMask": true, + "Conn": true, + "DNSConfigError": true, + "DNSError": true, + "DefaultResolver": true, + "Dial": true, + "DialIP": true, + "DialTCP": true, + "DialTimeout": true, + "DialUDP": true, + "DialUnix": true, + "Dialer": true, + "ErrWriteToConnected": true, + "Error": true, + "FileConn": true, + "FileListener": true, + "FilePacketConn": true, + "FlagBroadcast": true, + "FlagLoopback": true, + "FlagMulticast": true, + "FlagPointToPoint": true, + "FlagUp": true, + "Flags": true, + "HardwareAddr": true, + "IP": true, + "IPAddr": true, + "IPConn": true, + "IPMask": true, + "IPNet": true, + "IPv4": true, + "IPv4Mask": true, + "IPv4allrouter": true, + "IPv4allsys": true, + "IPv4bcast": true, + "IPv4len": true, + "IPv4zero": true, + "IPv6interfacelocalallnodes": true, + "IPv6len": true, + "IPv6linklocalallnodes": true, + "IPv6linklocalallrouters": true, + "IPv6loopback": true, + "IPv6unspecified": true, + "IPv6zero": true, + "Interface": true, + "InterfaceAddrs": true, + "InterfaceByIndex": true, + "InterfaceByName": true, + "Interfaces": true, + "InvalidAddrError": true, + "JoinHostPort": true, + "Listen": true, + "ListenConfig": true, + "ListenIP": true, + "ListenMulticastUDP": true, + "ListenPacket": true, + "ListenTCP": true, + "ListenUDP": true, + "ListenUnix": true, + "ListenUnixgram": true, + "Listener": true, + "LookupAddr": true, + "LookupCNAME": true, + "LookupHost": true, + "LookupIP": true, + "LookupMX": true, + "LookupNS": true, + "LookupPort": true, + "LookupSRV": true, + "LookupTXT": true, + "MX": true, + "NS": true, + "OpError": true, + "PacketConn": true, + "ParseCIDR": true, + "ParseError": true, + "ParseIP": true, + "ParseMAC": true, + "Pipe": true, + "ResolveIPAddr": true, + "ResolveTCPAddr": true, + "ResolveUDPAddr": true, + "ResolveUnixAddr": true, + "Resolver": true, + "SRV": true, + "SplitHostPort": true, + "TCPAddr": true, + "TCPConn": true, + "TCPListener": true, + "UDPAddr": true, + "UDPConn": true, + "UnixAddr": true, + "UnixConn": true, + "UnixListener": true, + "UnknownNetworkError": true, + }, + "net/http": map[string]bool{ + "CanonicalHeaderKey": true, + "Client": true, + "CloseNotifier": true, + "ConnState": true, + "Cookie": true, + "CookieJar": true, + "DefaultClient": true, + "DefaultMaxHeaderBytes": true, + "DefaultMaxIdleConnsPerHost": true, + "DefaultServeMux": true, + "DefaultTransport": true, + "DetectContentType": true, + "Dir": true, + "ErrAbortHandler": true, + "ErrBodyNotAllowed": true, + "ErrBodyReadAfterClose": true, + "ErrContentLength": true, + "ErrHandlerTimeout": true, + "ErrHeaderTooLong": true, + "ErrHijacked": true, + "ErrLineTooLong": true, + "ErrMissingBoundary": true, + "ErrMissingContentLength": true, + "ErrMissingFile": true, + "ErrNoCookie": true, + "ErrNoLocation": true, + "ErrNotMultipart": true, + "ErrNotSupported": true, + "ErrServerClosed": true, + "ErrShortBody": true, + "ErrSkipAltProtocol": true, + "ErrUnexpectedTrailer": true, + "ErrUseLastResponse": true, + "ErrWriteAfterFlush": true, + "Error": true, + "File": true, + "FileServer": true, + "FileSystem": true, + "Flusher": true, + "Get": true, + "Handle": true, + "HandleFunc": true, + "Handler": true, + "HandlerFunc": true, + "Head": true, + "Header": true, + "Hijacker": true, + "ListenAndServe": true, + "ListenAndServeTLS": true, + "LocalAddrContextKey": true, + "MaxBytesReader": true, + "MethodConnect": true, + "MethodDelete": true, + "MethodGet": true, + "MethodHead": true, + "MethodOptions": true, + "MethodPatch": true, + "MethodPost": true, + "MethodPut": true, + "MethodTrace": true, + "NewFileTransport": true, + "NewRequest": true, + "NewServeMux": true, + "NoBody": true, + "NotFound": true, + "NotFoundHandler": true, + "ParseHTTPVersion": true, + "ParseTime": true, + "Post": true, + "PostForm": true, + "ProtocolError": true, + "ProxyFromEnvironment": true, + "ProxyURL": true, + "PushOptions": true, + "Pusher": true, + "ReadRequest": true, + "ReadResponse": true, + "Redirect": true, + "RedirectHandler": true, + "Request": true, + "Response": true, + "ResponseWriter": true, + "RoundTripper": true, + "SameSite": true, + "SameSiteDefaultMode": true, + "SameSiteLaxMode": true, + "SameSiteStrictMode": true, + "Serve": true, + "ServeContent": true, + "ServeFile": true, + "ServeMux": true, + "ServeTLS": true, + "Server": true, + "ServerContextKey": true, + "SetCookie": true, + "StateActive": true, + "StateClosed": true, + "StateHijacked": true, + "StateIdle": true, + "StateNew": true, + "StatusAccepted": true, + "StatusAlreadyReported": true, + "StatusBadGateway": true, + "StatusBadRequest": true, + "StatusConflict": true, + "StatusContinue": true, + "StatusCreated": true, + "StatusExpectationFailed": true, + "StatusFailedDependency": true, + "StatusForbidden": true, + "StatusFound": true, + "StatusGatewayTimeout": true, + "StatusGone": true, + "StatusHTTPVersionNotSupported": true, + "StatusIMUsed": true, + "StatusInsufficientStorage": true, + "StatusInternalServerError": true, + "StatusLengthRequired": true, + "StatusLocked": true, + "StatusLoopDetected": true, + "StatusMethodNotAllowed": true, + "StatusMisdirectedRequest": true, + "StatusMovedPermanently": true, + "StatusMultiStatus": true, + "StatusMultipleChoices": true, + "StatusNetworkAuthenticationRequired": true, + "StatusNoContent": true, + "StatusNonAuthoritativeInfo": true, + "StatusNotAcceptable": true, + "StatusNotExtended": true, + "StatusNotFound": true, + "StatusNotImplemented": true, + "StatusNotModified": true, + "StatusOK": true, + "StatusPartialContent": true, + "StatusPaymentRequired": true, + "StatusPermanentRedirect": true, + "StatusPreconditionFailed": true, + "StatusPreconditionRequired": true, + "StatusProcessing": true, + "StatusProxyAuthRequired": true, + "StatusRequestEntityTooLarge": true, + "StatusRequestHeaderFieldsTooLarge": true, + "StatusRequestTimeout": true, + "StatusRequestURITooLong": true, + "StatusRequestedRangeNotSatisfiable": true, + "StatusResetContent": true, + "StatusSeeOther": true, + "StatusServiceUnavailable": true, + "StatusSwitchingProtocols": true, + "StatusTeapot": true, + "StatusTemporaryRedirect": true, + "StatusText": true, + "StatusTooEarly": true, + "StatusTooManyRequests": true, + "StatusUnauthorized": true, + "StatusUnavailableForLegalReasons": true, + "StatusUnprocessableEntity": true, + "StatusUnsupportedMediaType": true, + "StatusUpgradeRequired": true, + "StatusUseProxy": true, + "StatusVariantAlsoNegotiates": true, + "StripPrefix": true, + "TimeFormat": true, + "TimeoutHandler": true, + "TrailerPrefix": true, + "Transport": true, + }, + "net/http/cgi": map[string]bool{ + "Handler": true, + "Request": true, + "RequestFromMap": true, + "Serve": true, + }, + "net/http/cookiejar": map[string]bool{ + "Jar": true, + "New": true, + "Options": true, + "PublicSuffixList": true, + }, + "net/http/fcgi": map[string]bool{ + "ErrConnClosed": true, + "ErrRequestAborted": true, + "ProcessEnv": true, + "Serve": true, + }, + "net/http/httptest": map[string]bool{ + "DefaultRemoteAddr": true, + "NewRecorder": true, + "NewRequest": true, + "NewServer": true, + "NewTLSServer": true, + "NewUnstartedServer": true, + "ResponseRecorder": true, + "Server": true, + }, + "net/http/httptrace": map[string]bool{ + "ClientTrace": true, + "ContextClientTrace": true, + "DNSDoneInfo": true, + "DNSStartInfo": true, + "GotConnInfo": true, + "WithClientTrace": true, + "WroteRequestInfo": true, + }, + "net/http/httputil": map[string]bool{ + "BufferPool": true, + "ClientConn": true, + "DumpRequest": true, + "DumpRequestOut": true, + "DumpResponse": true, + "ErrClosed": true, + "ErrLineTooLong": true, + "ErrPersistEOF": true, + "ErrPipeline": true, + "NewChunkedReader": true, + "NewChunkedWriter": true, + "NewClientConn": true, + "NewProxyClientConn": true, + "NewServerConn": true, + "NewSingleHostReverseProxy": true, + "ReverseProxy": true, + "ServerConn": true, + }, + "net/http/pprof": map[string]bool{ + "Cmdline": true, + "Handler": true, + "Index": true, + "Profile": true, + "Symbol": true, + "Trace": true, + }, + "net/mail": map[string]bool{ + "Address": true, + "AddressParser": true, + "ErrHeaderNotPresent": true, + "Header": true, + "Message": true, + "ParseAddress": true, + "ParseAddressList": true, + "ParseDate": true, + "ReadMessage": true, + }, + "net/rpc": map[string]bool{ + "Accept": true, + "Call": true, + "Client": true, + "ClientCodec": true, + "DefaultDebugPath": true, + "DefaultRPCPath": true, + "DefaultServer": true, + "Dial": true, + "DialHTTP": true, + "DialHTTPPath": true, + "ErrShutdown": true, + "HandleHTTP": true, + "NewClient": true, + "NewClientWithCodec": true, + "NewServer": true, + "Register": true, + "RegisterName": true, + "Request": true, + "Response": true, + "ServeCodec": true, + "ServeConn": true, + "ServeRequest": true, + "Server": true, + "ServerCodec": true, + "ServerError": true, + }, + "net/rpc/jsonrpc": map[string]bool{ + "Dial": true, + "NewClient": true, + "NewClientCodec": true, + "NewServerCodec": true, + "ServeConn": true, + }, + "net/smtp": map[string]bool{ + "Auth": true, + "CRAMMD5Auth": true, + "Client": true, + "Dial": true, + "NewClient": true, + "PlainAuth": true, + "SendMail": true, + "ServerInfo": true, + }, + "net/textproto": map[string]bool{ + "CanonicalMIMEHeaderKey": true, + "Conn": true, + "Dial": true, + "Error": true, + "MIMEHeader": true, + "NewConn": true, + "NewReader": true, + "NewWriter": true, + "Pipeline": true, + "ProtocolError": true, + "Reader": true, + "TrimBytes": true, + "TrimString": true, + "Writer": true, + }, + "net/url": map[string]bool{ + "Error": true, + "EscapeError": true, + "InvalidHostError": true, + "Parse": true, + "ParseQuery": true, + "ParseRequestURI": true, + "PathEscape": true, + "PathUnescape": true, + "QueryEscape": true, + "QueryUnescape": true, + "URL": true, + "User": true, + "UserPassword": true, + "Userinfo": true, + "Values": true, + }, + "os": map[string]bool{ + "Args": true, + "Chdir": true, + "Chmod": true, + "Chown": true, + "Chtimes": true, + "Clearenv": true, + "Create": true, + "DevNull": true, + "Environ": true, + "ErrClosed": true, + "ErrExist": true, + "ErrInvalid": true, + "ErrNoDeadline": true, + "ErrNotExist": true, + "ErrPermission": true, + "Executable": true, + "Exit": true, + "Expand": true, + "ExpandEnv": true, + "File": true, + "FileInfo": true, + "FileMode": true, + "FindProcess": true, + "Getegid": true, + "Getenv": true, + "Geteuid": true, + "Getgid": true, + "Getgroups": true, + "Getpagesize": true, + "Getpid": true, + "Getppid": true, + "Getuid": true, + "Getwd": true, + "Hostname": true, + "Interrupt": true, + "IsExist": true, + "IsNotExist": true, + "IsPathSeparator": true, + "IsPermission": true, + "IsTimeout": true, + "Kill": true, + "Lchown": true, + "Link": true, + "LinkError": true, + "LookupEnv": true, + "Lstat": true, + "Mkdir": true, + "MkdirAll": true, + "ModeAppend": true, + "ModeCharDevice": true, + "ModeDevice": true, + "ModeDir": true, + "ModeExclusive": true, + "ModeIrregular": true, + "ModeNamedPipe": true, + "ModePerm": true, + "ModeSetgid": true, + "ModeSetuid": true, + "ModeSocket": true, + "ModeSticky": true, + "ModeSymlink": true, + "ModeTemporary": true, + "ModeType": true, + "NewFile": true, + "NewSyscallError": true, + "O_APPEND": true, + "O_CREATE": true, + "O_EXCL": true, + "O_RDONLY": true, + "O_RDWR": true, + "O_SYNC": true, + "O_TRUNC": true, + "O_WRONLY": true, + "Open": true, + "OpenFile": true, + "PathError": true, + "PathListSeparator": true, + "PathSeparator": true, + "Pipe": true, + "ProcAttr": true, + "Process": true, + "ProcessState": true, + "Readlink": true, + "Remove": true, + "RemoveAll": true, + "Rename": true, + "SEEK_CUR": true, + "SEEK_END": true, + "SEEK_SET": true, + "SameFile": true, + "Setenv": true, + "Signal": true, + "StartProcess": true, + "Stat": true, + "Stderr": true, + "Stdin": true, + "Stdout": true, + "Symlink": true, + "SyscallError": true, + "TempDir": true, + "Truncate": true, + "Unsetenv": true, + "UserCacheDir": true, + "UserHomeDir": true, + }, + "os/exec": map[string]bool{ + "Cmd": true, + "Command": true, + "CommandContext": true, + "ErrNotFound": true, + "Error": true, + "ExitError": true, + "LookPath": true, + }, + "os/signal": map[string]bool{ + "Ignore": true, + "Ignored": true, + "Notify": true, + "Reset": true, + "Stop": true, + }, + "os/user": map[string]bool{ + "Current": true, + "Group": true, + "Lookup": true, + "LookupGroup": true, + "LookupGroupId": true, + "LookupId": true, + "UnknownGroupError": true, + "UnknownGroupIdError": true, + "UnknownUserError": true, + "UnknownUserIdError": true, + "User": true, + }, + "path": map[string]bool{ + "Base": true, + "Clean": true, + "Dir": true, + "ErrBadPattern": true, + "Ext": true, + "IsAbs": true, + "Join": true, + "Match": true, + "Split": true, + }, + "path/filepath": map[string]bool{ + "Abs": true, + "Base": true, + "Clean": true, + "Dir": true, + "ErrBadPattern": true, + "EvalSymlinks": true, + "Ext": true, + "FromSlash": true, + "Glob": true, + "HasPrefix": true, + "IsAbs": true, + "Join": true, + "ListSeparator": true, + "Match": true, + "Rel": true, + "Separator": true, + "SkipDir": true, + "Split": true, + "SplitList": true, + "ToSlash": true, + "VolumeName": true, + "Walk": true, + "WalkFunc": true, + }, + "plugin": map[string]bool{ + "Open": true, + "Plugin": true, + "Symbol": true, + }, + "reflect": map[string]bool{ + "Append": true, + "AppendSlice": true, + "Array": true, + "ArrayOf": true, + "Bool": true, + "BothDir": true, + "Chan": true, + "ChanDir": true, + "ChanOf": true, + "Complex128": true, + "Complex64": true, + "Copy": true, + "DeepEqual": true, + "Float32": true, + "Float64": true, + "Func": true, + "FuncOf": true, + "Indirect": true, + "Int": true, + "Int16": true, + "Int32": true, + "Int64": true, + "Int8": true, + "Interface": true, + "Invalid": true, + "Kind": true, + "MakeChan": true, + "MakeFunc": true, + "MakeMap": true, + "MakeMapWithSize": true, + "MakeSlice": true, + "Map": true, + "MapIter": true, + "MapOf": true, + "Method": true, + "New": true, + "NewAt": true, + "Ptr": true, + "PtrTo": true, + "RecvDir": true, + "Select": true, + "SelectCase": true, + "SelectDefault": true, + "SelectDir": true, + "SelectRecv": true, + "SelectSend": true, + "SendDir": true, + "Slice": true, + "SliceHeader": true, + "SliceOf": true, + "String": true, + "StringHeader": true, + "Struct": true, + "StructField": true, + "StructOf": true, + "StructTag": true, + "Swapper": true, + "TypeOf": true, + "Uint": true, + "Uint16": true, + "Uint32": true, + "Uint64": true, + "Uint8": true, + "Uintptr": true, + "UnsafePointer": true, + "Value": true, + "ValueError": true, + "ValueOf": true, + "Zero": true, + }, + "regexp": map[string]bool{ + "Compile": true, + "CompilePOSIX": true, + "Match": true, + "MatchReader": true, + "MatchString": true, + "MustCompile": true, + "MustCompilePOSIX": true, + "QuoteMeta": true, + "Regexp": true, + }, + "regexp/syntax": map[string]bool{ + "ClassNL": true, + "Compile": true, + "DotNL": true, + "EmptyBeginLine": true, + "EmptyBeginText": true, + "EmptyEndLine": true, + "EmptyEndText": true, + "EmptyNoWordBoundary": true, + "EmptyOp": true, + "EmptyOpContext": true, + "EmptyWordBoundary": true, + "ErrInternalError": true, + "ErrInvalidCharClass": true, + "ErrInvalidCharRange": true, + "ErrInvalidEscape": true, + "ErrInvalidNamedCapture": true, + "ErrInvalidPerlOp": true, + "ErrInvalidRepeatOp": true, + "ErrInvalidRepeatSize": true, + "ErrInvalidUTF8": true, + "ErrMissingBracket": true, + "ErrMissingParen": true, + "ErrMissingRepeatArgument": true, + "ErrTrailingBackslash": true, + "ErrUnexpectedParen": true, + "Error": true, + "ErrorCode": true, + "Flags": true, + "FoldCase": true, + "Inst": true, + "InstAlt": true, + "InstAltMatch": true, + "InstCapture": true, + "InstEmptyWidth": true, + "InstFail": true, + "InstMatch": true, + "InstNop": true, + "InstOp": true, + "InstRune": true, + "InstRune1": true, + "InstRuneAny": true, + "InstRuneAnyNotNL": true, + "IsWordChar": true, + "Literal": true, + "MatchNL": true, + "NonGreedy": true, + "OneLine": true, + "Op": true, + "OpAlternate": true, + "OpAnyChar": true, + "OpAnyCharNotNL": true, + "OpBeginLine": true, + "OpBeginText": true, + "OpCapture": true, + "OpCharClass": true, + "OpConcat": true, + "OpEmptyMatch": true, + "OpEndLine": true, + "OpEndText": true, + "OpLiteral": true, + "OpNoMatch": true, + "OpNoWordBoundary": true, + "OpPlus": true, + "OpQuest": true, + "OpRepeat": true, + "OpStar": true, + "OpWordBoundary": true, + "POSIX": true, + "Parse": true, + "Perl": true, + "PerlX": true, + "Prog": true, + "Regexp": true, + "Simple": true, + "UnicodeGroups": true, + "WasDollar": true, + }, + "runtime": map[string]bool{ + "BlockProfile": true, + "BlockProfileRecord": true, + "Breakpoint": true, + "CPUProfile": true, + "Caller": true, + "Callers": true, + "CallersFrames": true, + "Compiler": true, + "Error": true, + "Frame": true, + "Frames": true, + "Func": true, + "FuncForPC": true, + "GC": true, + "GOARCH": true, + "GOMAXPROCS": true, + "GOOS": true, + "GOROOT": true, + "Goexit": true, + "GoroutineProfile": true, + "Gosched": true, + "KeepAlive": true, + "LockOSThread": true, + "MemProfile": true, + "MemProfileRate": true, + "MemProfileRecord": true, + "MemStats": true, + "MutexProfile": true, + "NumCPU": true, + "NumCgoCall": true, + "NumGoroutine": true, + "ReadMemStats": true, + "ReadTrace": true, + "SetBlockProfileRate": true, + "SetCPUProfileRate": true, + "SetCgoTraceback": true, + "SetFinalizer": true, + "SetMutexProfileFraction": true, + "Stack": true, + "StackRecord": true, + "StartTrace": true, + "StopTrace": true, + "ThreadCreateProfile": true, + "TypeAssertionError": true, + "UnlockOSThread": true, + "Version": true, + }, + "runtime/debug": map[string]bool{ + "BuildInfo": true, + "FreeOSMemory": true, + "GCStats": true, + "Module": true, + "PrintStack": true, + "ReadBuildInfo": true, + "ReadGCStats": true, + "SetGCPercent": true, + "SetMaxStack": true, + "SetMaxThreads": true, + "SetPanicOnFault": true, + "SetTraceback": true, + "Stack": true, + "WriteHeapDump": true, + }, + "runtime/pprof": map[string]bool{ + "Do": true, + "ForLabels": true, + "Label": true, + "LabelSet": true, + "Labels": true, + "Lookup": true, + "NewProfile": true, + "Profile": true, + "Profiles": true, + "SetGoroutineLabels": true, + "StartCPUProfile": true, + "StopCPUProfile": true, + "WithLabels": true, + "WriteHeapProfile": true, + }, + "runtime/trace": map[string]bool{ + "IsEnabled": true, + "Log": true, + "Logf": true, + "NewTask": true, + "Region": true, + "Start": true, + "StartRegion": true, + "Stop": true, + "Task": true, + "WithRegion": true, + }, + "sort": map[string]bool{ + "Float64Slice": true, + "Float64s": true, + "Float64sAreSorted": true, + "IntSlice": true, + "Interface": true, + "Ints": true, + "IntsAreSorted": true, + "IsSorted": true, + "Reverse": true, + "Search": true, + "SearchFloat64s": true, + "SearchInts": true, + "SearchStrings": true, + "Slice": true, + "SliceIsSorted": true, + "SliceStable": true, + "Sort": true, + "Stable": true, + "StringSlice": true, + "Strings": true, + "StringsAreSorted": true, + }, + "strconv": map[string]bool{ + "AppendBool": true, + "AppendFloat": true, + "AppendInt": true, + "AppendQuote": true, + "AppendQuoteRune": true, + "AppendQuoteRuneToASCII": true, + "AppendQuoteRuneToGraphic": true, + "AppendQuoteToASCII": true, + "AppendQuoteToGraphic": true, + "AppendUint": true, + "Atoi": true, + "CanBackquote": true, + "ErrRange": true, + "ErrSyntax": true, + "FormatBool": true, + "FormatFloat": true, + "FormatInt": true, + "FormatUint": true, + "IntSize": true, + "IsGraphic": true, + "IsPrint": true, + "Itoa": true, + "NumError": true, + "ParseBool": true, + "ParseFloat": true, + "ParseInt": true, + "ParseUint": true, + "Quote": true, + "QuoteRune": true, + "QuoteRuneToASCII": true, + "QuoteRuneToGraphic": true, + "QuoteToASCII": true, + "QuoteToGraphic": true, + "Unquote": true, + "UnquoteChar": true, + }, + "strings": map[string]bool{ + "Builder": true, + "Compare": true, + "Contains": true, + "ContainsAny": true, + "ContainsRune": true, + "Count": true, + "EqualFold": true, + "Fields": true, + "FieldsFunc": true, + "HasPrefix": true, + "HasSuffix": true, + "Index": true, + "IndexAny": true, + "IndexByte": true, + "IndexFunc": true, + "IndexRune": true, + "Join": true, + "LastIndex": true, + "LastIndexAny": true, + "LastIndexByte": true, + "LastIndexFunc": true, + "Map": true, + "NewReader": true, + "NewReplacer": true, + "Reader": true, + "Repeat": true, + "Replace": true, + "ReplaceAll": true, + "Replacer": true, + "Split": true, + "SplitAfter": true, + "SplitAfterN": true, + "SplitN": true, + "Title": true, + "ToLower": true, + "ToLowerSpecial": true, + "ToTitle": true, + "ToTitleSpecial": true, + "ToUpper": true, + "ToUpperSpecial": true, + "Trim": true, + "TrimFunc": true, + "TrimLeft": true, + "TrimLeftFunc": true, + "TrimPrefix": true, + "TrimRight": true, + "TrimRightFunc": true, + "TrimSpace": true, + "TrimSuffix": true, + }, + "sync": map[string]bool{ + "Cond": true, + "Locker": true, + "Map": true, + "Mutex": true, + "NewCond": true, + "Once": true, + "Pool": true, + "RWMutex": true, + "WaitGroup": true, + }, + "sync/atomic": map[string]bool{ + "AddInt32": true, + "AddInt64": true, + "AddUint32": true, + "AddUint64": true, + "AddUintptr": true, + "CompareAndSwapInt32": true, + "CompareAndSwapInt64": true, + "CompareAndSwapPointer": true, + "CompareAndSwapUint32": true, + "CompareAndSwapUint64": true, + "CompareAndSwapUintptr": true, + "LoadInt32": true, + "LoadInt64": true, + "LoadPointer": true, + "LoadUint32": true, + "LoadUint64": true, + "LoadUintptr": true, + "StoreInt32": true, + "StoreInt64": true, + "StorePointer": true, + "StoreUint32": true, + "StoreUint64": true, + "StoreUintptr": true, + "SwapInt32": true, + "SwapInt64": true, + "SwapPointer": true, + "SwapUint32": true, + "SwapUint64": true, + "SwapUintptr": true, + "Value": true, + }, + "syscall": map[string]bool{ + "AF_ALG": true, + "AF_APPLETALK": true, + "AF_ARP": true, + "AF_ASH": true, + "AF_ATM": true, + "AF_ATMPVC": true, + "AF_ATMSVC": true, + "AF_AX25": true, + "AF_BLUETOOTH": true, + "AF_BRIDGE": true, + "AF_CAIF": true, + "AF_CAN": true, + "AF_CCITT": true, + "AF_CHAOS": true, + "AF_CNT": true, + "AF_COIP": true, + "AF_DATAKIT": true, + "AF_DECnet": true, + "AF_DLI": true, + "AF_E164": true, + "AF_ECMA": true, + "AF_ECONET": true, + "AF_ENCAP": true, + "AF_FILE": true, + "AF_HYLINK": true, + "AF_IEEE80211": true, + "AF_IEEE802154": true, + "AF_IMPLINK": true, + "AF_INET": true, + "AF_INET6": true, + "AF_INET6_SDP": true, + "AF_INET_SDP": true, + "AF_IPX": true, + "AF_IRDA": true, + "AF_ISDN": true, + "AF_ISO": true, + "AF_IUCV": true, + "AF_KEY": true, + "AF_LAT": true, + "AF_LINK": true, + "AF_LLC": true, + "AF_LOCAL": true, + "AF_MAX": true, + "AF_MPLS": true, + "AF_NATM": true, + "AF_NDRV": true, + "AF_NETBEUI": true, + "AF_NETBIOS": true, + "AF_NETGRAPH": true, + "AF_NETLINK": true, + "AF_NETROM": true, + "AF_NS": true, + "AF_OROUTE": true, + "AF_OSI": true, + "AF_PACKET": true, + "AF_PHONET": true, + "AF_PPP": true, + "AF_PPPOX": true, + "AF_PUP": true, + "AF_RDS": true, + "AF_RESERVED_36": true, + "AF_ROSE": true, + "AF_ROUTE": true, + "AF_RXRPC": true, + "AF_SCLUSTER": true, + "AF_SECURITY": true, + "AF_SIP": true, + "AF_SLOW": true, + "AF_SNA": true, + "AF_SYSTEM": true, + "AF_TIPC": true, + "AF_UNIX": true, + "AF_UNSPEC": true, + "AF_VENDOR00": true, + "AF_VENDOR01": true, + "AF_VENDOR02": true, + "AF_VENDOR03": true, + "AF_VENDOR04": true, + "AF_VENDOR05": true, + "AF_VENDOR06": true, + "AF_VENDOR07": true, + "AF_VENDOR08": true, + "AF_VENDOR09": true, + "AF_VENDOR10": true, + "AF_VENDOR11": true, + "AF_VENDOR12": true, + "AF_VENDOR13": true, + "AF_VENDOR14": true, + "AF_VENDOR15": true, + "AF_VENDOR16": true, + "AF_VENDOR17": true, + "AF_VENDOR18": true, + "AF_VENDOR19": true, + "AF_VENDOR20": true, + "AF_VENDOR21": true, + "AF_VENDOR22": true, + "AF_VENDOR23": true, + "AF_VENDOR24": true, + "AF_VENDOR25": true, + "AF_VENDOR26": true, + "AF_VENDOR27": true, + "AF_VENDOR28": true, + "AF_VENDOR29": true, + "AF_VENDOR30": true, + "AF_VENDOR31": true, + "AF_VENDOR32": true, + "AF_VENDOR33": true, + "AF_VENDOR34": true, + "AF_VENDOR35": true, + "AF_VENDOR36": true, + "AF_VENDOR37": true, + "AF_VENDOR38": true, + "AF_VENDOR39": true, + "AF_VENDOR40": true, + "AF_VENDOR41": true, + "AF_VENDOR42": true, + "AF_VENDOR43": true, + "AF_VENDOR44": true, + "AF_VENDOR45": true, + "AF_VENDOR46": true, + "AF_VENDOR47": true, + "AF_WANPIPE": true, + "AF_X25": true, + "AI_CANONNAME": true, + "AI_NUMERICHOST": true, + "AI_PASSIVE": true, + "APPLICATION_ERROR": true, + "ARPHRD_ADAPT": true, + "ARPHRD_APPLETLK": true, + "ARPHRD_ARCNET": true, + "ARPHRD_ASH": true, + "ARPHRD_ATM": true, + "ARPHRD_AX25": true, + "ARPHRD_BIF": true, + "ARPHRD_CHAOS": true, + "ARPHRD_CISCO": true, + "ARPHRD_CSLIP": true, + "ARPHRD_CSLIP6": true, + "ARPHRD_DDCMP": true, + "ARPHRD_DLCI": true, + "ARPHRD_ECONET": true, + "ARPHRD_EETHER": true, + "ARPHRD_ETHER": true, + "ARPHRD_EUI64": true, + "ARPHRD_FCAL": true, + "ARPHRD_FCFABRIC": true, + "ARPHRD_FCPL": true, + "ARPHRD_FCPP": true, + "ARPHRD_FDDI": true, + "ARPHRD_FRAD": true, + "ARPHRD_FRELAY": true, + "ARPHRD_HDLC": true, + "ARPHRD_HIPPI": true, + "ARPHRD_HWX25": true, + "ARPHRD_IEEE1394": true, + "ARPHRD_IEEE802": true, + "ARPHRD_IEEE80211": true, + "ARPHRD_IEEE80211_PRISM": true, + "ARPHRD_IEEE80211_RADIOTAP": true, + "ARPHRD_IEEE802154": true, + "ARPHRD_IEEE802154_PHY": true, + "ARPHRD_IEEE802_TR": true, + "ARPHRD_INFINIBAND": true, + "ARPHRD_IPDDP": true, + "ARPHRD_IPGRE": true, + "ARPHRD_IRDA": true, + "ARPHRD_LAPB": true, + "ARPHRD_LOCALTLK": true, + "ARPHRD_LOOPBACK": true, + "ARPHRD_METRICOM": true, + "ARPHRD_NETROM": true, + "ARPHRD_NONE": true, + "ARPHRD_PIMREG": true, + "ARPHRD_PPP": true, + "ARPHRD_PRONET": true, + "ARPHRD_RAWHDLC": true, + "ARPHRD_ROSE": true, + "ARPHRD_RSRVD": true, + "ARPHRD_SIT": true, + "ARPHRD_SKIP": true, + "ARPHRD_SLIP": true, + "ARPHRD_SLIP6": true, + "ARPHRD_STRIP": true, + "ARPHRD_TUNNEL": true, + "ARPHRD_TUNNEL6": true, + "ARPHRD_VOID": true, + "ARPHRD_X25": true, + "AUTHTYPE_CLIENT": true, + "AUTHTYPE_SERVER": true, + "Accept": true, + "Accept4": true, + "AcceptEx": true, + "Access": true, + "Acct": true, + "AddrinfoW": true, + "Adjtime": true, + "Adjtimex": true, + "AttachLsf": true, + "B0": true, + "B1000000": true, + "B110": true, + "B115200": true, + "B1152000": true, + "B1200": true, + "B134": true, + "B14400": true, + "B150": true, + "B1500000": true, + "B1800": true, + "B19200": true, + "B200": true, + "B2000000": true, + "B230400": true, + "B2400": true, + "B2500000": true, + "B28800": true, + "B300": true, + "B3000000": true, + "B3500000": true, + "B38400": true, + "B4000000": true, + "B460800": true, + "B4800": true, + "B50": true, + "B500000": true, + "B57600": true, + "B576000": true, + "B600": true, + "B7200": true, + "B75": true, + "B76800": true, + "B921600": true, + "B9600": true, + "BASE_PROTOCOL": true, + "BIOCFEEDBACK": true, + "BIOCFLUSH": true, + "BIOCGBLEN": true, + "BIOCGDIRECTION": true, + "BIOCGDIRFILT": true, + "BIOCGDLT": true, + "BIOCGDLTLIST": true, + "BIOCGETBUFMODE": true, + "BIOCGETIF": true, + "BIOCGETZMAX": true, + "BIOCGFEEDBACK": true, + "BIOCGFILDROP": true, + "BIOCGHDRCMPLT": true, + "BIOCGRSIG": true, + "BIOCGRTIMEOUT": true, + "BIOCGSEESENT": true, + "BIOCGSTATS": true, + "BIOCGSTATSOLD": true, + "BIOCGTSTAMP": true, + "BIOCIMMEDIATE": true, + "BIOCLOCK": true, + "BIOCPROMISC": true, + "BIOCROTZBUF": true, + "BIOCSBLEN": true, + "BIOCSDIRECTION": true, + "BIOCSDIRFILT": true, + "BIOCSDLT": true, + "BIOCSETBUFMODE": true, + "BIOCSETF": true, + "BIOCSETFNR": true, + "BIOCSETIF": true, + "BIOCSETWF": true, + "BIOCSETZBUF": true, + "BIOCSFEEDBACK": true, + "BIOCSFILDROP": true, + "BIOCSHDRCMPLT": true, + "BIOCSRSIG": true, + "BIOCSRTIMEOUT": true, + "BIOCSSEESENT": true, + "BIOCSTCPF": true, + "BIOCSTSTAMP": true, + "BIOCSUDPF": true, + "BIOCVERSION": true, + "BPF_A": true, + "BPF_ABS": true, + "BPF_ADD": true, + "BPF_ALIGNMENT": true, + "BPF_ALIGNMENT32": true, + "BPF_ALU": true, + "BPF_AND": true, + "BPF_B": true, + "BPF_BUFMODE_BUFFER": true, + "BPF_BUFMODE_ZBUF": true, + "BPF_DFLTBUFSIZE": true, + "BPF_DIRECTION_IN": true, + "BPF_DIRECTION_OUT": true, + "BPF_DIV": true, + "BPF_H": true, + "BPF_IMM": true, + "BPF_IND": true, + "BPF_JA": true, + "BPF_JEQ": true, + "BPF_JGE": true, + "BPF_JGT": true, + "BPF_JMP": true, + "BPF_JSET": true, + "BPF_K": true, + "BPF_LD": true, + "BPF_LDX": true, + "BPF_LEN": true, + "BPF_LSH": true, + "BPF_MAJOR_VERSION": true, + "BPF_MAXBUFSIZE": true, + "BPF_MAXINSNS": true, + "BPF_MEM": true, + "BPF_MEMWORDS": true, + "BPF_MINBUFSIZE": true, + "BPF_MINOR_VERSION": true, + "BPF_MISC": true, + "BPF_MSH": true, + "BPF_MUL": true, + "BPF_NEG": true, + "BPF_OR": true, + "BPF_RELEASE": true, + "BPF_RET": true, + "BPF_RSH": true, + "BPF_ST": true, + "BPF_STX": true, + "BPF_SUB": true, + "BPF_TAX": true, + "BPF_TXA": true, + "BPF_T_BINTIME": true, + "BPF_T_BINTIME_FAST": true, + "BPF_T_BINTIME_MONOTONIC": true, + "BPF_T_BINTIME_MONOTONIC_FAST": true, + "BPF_T_FAST": true, + "BPF_T_FLAG_MASK": true, + "BPF_T_FORMAT_MASK": true, + "BPF_T_MICROTIME": true, + "BPF_T_MICROTIME_FAST": true, + "BPF_T_MICROTIME_MONOTONIC": true, + "BPF_T_MICROTIME_MONOTONIC_FAST": true, + "BPF_T_MONOTONIC": true, + "BPF_T_MONOTONIC_FAST": true, + "BPF_T_NANOTIME": true, + "BPF_T_NANOTIME_FAST": true, + "BPF_T_NANOTIME_MONOTONIC": true, + "BPF_T_NANOTIME_MONOTONIC_FAST": true, + "BPF_T_NONE": true, + "BPF_T_NORMAL": true, + "BPF_W": true, + "BPF_X": true, + "BRKINT": true, + "Bind": true, + "BindToDevice": true, + "BpfBuflen": true, + "BpfDatalink": true, + "BpfHdr": true, + "BpfHeadercmpl": true, + "BpfInsn": true, + "BpfInterface": true, + "BpfJump": true, + "BpfProgram": true, + "BpfStat": true, + "BpfStats": true, + "BpfStmt": true, + "BpfTimeout": true, + "BpfTimeval": true, + "BpfVersion": true, + "BpfZbuf": true, + "BpfZbufHeader": true, + "ByHandleFileInformation": true, + "BytePtrFromString": true, + "ByteSliceFromString": true, + "CCR0_FLUSH": true, + "CERT_CHAIN_POLICY_AUTHENTICODE": true, + "CERT_CHAIN_POLICY_AUTHENTICODE_TS": true, + "CERT_CHAIN_POLICY_BASE": true, + "CERT_CHAIN_POLICY_BASIC_CONSTRAINTS": true, + "CERT_CHAIN_POLICY_EV": true, + "CERT_CHAIN_POLICY_MICROSOFT_ROOT": true, + "CERT_CHAIN_POLICY_NT_AUTH": true, + "CERT_CHAIN_POLICY_SSL": true, + "CERT_E_CN_NO_MATCH": true, + "CERT_E_EXPIRED": true, + "CERT_E_PURPOSE": true, + "CERT_E_ROLE": true, + "CERT_E_UNTRUSTEDROOT": true, + "CERT_STORE_ADD_ALWAYS": true, + "CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG": true, + "CERT_STORE_PROV_MEMORY": true, + "CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT": true, + "CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT": true, + "CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT": true, + "CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT": true, + "CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT": true, + "CERT_TRUST_INVALID_BASIC_CONSTRAINTS": true, + "CERT_TRUST_INVALID_EXTENSION": true, + "CERT_TRUST_INVALID_NAME_CONSTRAINTS": true, + "CERT_TRUST_INVALID_POLICY_CONSTRAINTS": true, + "CERT_TRUST_IS_CYCLIC": true, + "CERT_TRUST_IS_EXPLICIT_DISTRUST": true, + "CERT_TRUST_IS_NOT_SIGNATURE_VALID": true, + "CERT_TRUST_IS_NOT_TIME_VALID": true, + "CERT_TRUST_IS_NOT_VALID_FOR_USAGE": true, + "CERT_TRUST_IS_OFFLINE_REVOCATION": true, + "CERT_TRUST_IS_REVOKED": true, + "CERT_TRUST_IS_UNTRUSTED_ROOT": true, + "CERT_TRUST_NO_ERROR": true, + "CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY": true, + "CERT_TRUST_REVOCATION_STATUS_UNKNOWN": true, + "CFLUSH": true, + "CLOCAL": true, + "CLONE_CHILD_CLEARTID": true, + "CLONE_CHILD_SETTID": true, + "CLONE_CSIGNAL": true, + "CLONE_DETACHED": true, + "CLONE_FILES": true, + "CLONE_FS": true, + "CLONE_IO": true, + "CLONE_NEWIPC": true, + "CLONE_NEWNET": true, + "CLONE_NEWNS": true, + "CLONE_NEWPID": true, + "CLONE_NEWUSER": true, + "CLONE_NEWUTS": true, + "CLONE_PARENT": true, + "CLONE_PARENT_SETTID": true, + "CLONE_PID": true, + "CLONE_PTRACE": true, + "CLONE_SETTLS": true, + "CLONE_SIGHAND": true, + "CLONE_SYSVSEM": true, + "CLONE_THREAD": true, + "CLONE_UNTRACED": true, + "CLONE_VFORK": true, + "CLONE_VM": true, + "CPUID_CFLUSH": true, + "CREAD": true, + "CREATE_ALWAYS": true, + "CREATE_NEW": true, + "CREATE_NEW_PROCESS_GROUP": true, + "CREATE_UNICODE_ENVIRONMENT": true, + "CRYPT_DEFAULT_CONTAINER_OPTIONAL": true, + "CRYPT_DELETEKEYSET": true, + "CRYPT_MACHINE_KEYSET": true, + "CRYPT_NEWKEYSET": true, + "CRYPT_SILENT": true, + "CRYPT_VERIFYCONTEXT": true, + "CS5": true, + "CS6": true, + "CS7": true, + "CS8": true, + "CSIZE": true, + "CSTART": true, + "CSTATUS": true, + "CSTOP": true, + "CSTOPB": true, + "CSUSP": true, + "CTL_MAXNAME": true, + "CTL_NET": true, + "CTL_QUERY": true, + "CTRL_BREAK_EVENT": true, + "CTRL_C_EVENT": true, + "CancelIo": true, + "CancelIoEx": true, + "CertAddCertificateContextToStore": true, + "CertChainContext": true, + "CertChainElement": true, + "CertChainPara": true, + "CertChainPolicyPara": true, + "CertChainPolicyStatus": true, + "CertCloseStore": true, + "CertContext": true, + "CertCreateCertificateContext": true, + "CertEnhKeyUsage": true, + "CertEnumCertificatesInStore": true, + "CertFreeCertificateChain": true, + "CertFreeCertificateContext": true, + "CertGetCertificateChain": true, + "CertInfo": true, + "CertOpenStore": true, + "CertOpenSystemStore": true, + "CertRevocationCrlInfo": true, + "CertRevocationInfo": true, + "CertSimpleChain": true, + "CertTrustListInfo": true, + "CertTrustStatus": true, + "CertUsageMatch": true, + "CertVerifyCertificateChainPolicy": true, + "Chdir": true, + "CheckBpfVersion": true, + "Chflags": true, + "Chmod": true, + "Chown": true, + "Chroot": true, + "Clearenv": true, + "Close": true, + "CloseHandle": true, + "CloseOnExec": true, + "Closesocket": true, + "CmsgLen": true, + "CmsgSpace": true, + "Cmsghdr": true, + "CommandLineToArgv": true, + "ComputerName": true, + "Conn": true, + "Connect": true, + "ConnectEx": true, + "ConvertSidToStringSid": true, + "ConvertStringSidToSid": true, + "CopySid": true, + "Creat": true, + "CreateDirectory": true, + "CreateFile": true, + "CreateFileMapping": true, + "CreateHardLink": true, + "CreateIoCompletionPort": true, + "CreatePipe": true, + "CreateProcess": true, + "CreateProcessAsUser": true, + "CreateSymbolicLink": true, + "CreateToolhelp32Snapshot": true, + "Credential": true, + "CryptAcquireContext": true, + "CryptGenRandom": true, + "CryptReleaseContext": true, + "DIOCBSFLUSH": true, + "DIOCOSFPFLUSH": true, + "DLL": true, + "DLLError": true, + "DLT_A429": true, + "DLT_A653_ICM": true, + "DLT_AIRONET_HEADER": true, + "DLT_AOS": true, + "DLT_APPLE_IP_OVER_IEEE1394": true, + "DLT_ARCNET": true, + "DLT_ARCNET_LINUX": true, + "DLT_ATM_CLIP": true, + "DLT_ATM_RFC1483": true, + "DLT_AURORA": true, + "DLT_AX25": true, + "DLT_AX25_KISS": true, + "DLT_BACNET_MS_TP": true, + "DLT_BLUETOOTH_HCI_H4": true, + "DLT_BLUETOOTH_HCI_H4_WITH_PHDR": true, + "DLT_CAN20B": true, + "DLT_CAN_SOCKETCAN": true, + "DLT_CHAOS": true, + "DLT_CHDLC": true, + "DLT_CISCO_IOS": true, + "DLT_C_HDLC": true, + "DLT_C_HDLC_WITH_DIR": true, + "DLT_DBUS": true, + "DLT_DECT": true, + "DLT_DOCSIS": true, + "DLT_DVB_CI": true, + "DLT_ECONET": true, + "DLT_EN10MB": true, + "DLT_EN3MB": true, + "DLT_ENC": true, + "DLT_ERF": true, + "DLT_ERF_ETH": true, + "DLT_ERF_POS": true, + "DLT_FC_2": true, + "DLT_FC_2_WITH_FRAME_DELIMS": true, + "DLT_FDDI": true, + "DLT_FLEXRAY": true, + "DLT_FRELAY": true, + "DLT_FRELAY_WITH_DIR": true, + "DLT_GCOM_SERIAL": true, + "DLT_GCOM_T1E1": true, + "DLT_GPF_F": true, + "DLT_GPF_T": true, + "DLT_GPRS_LLC": true, + "DLT_GSMTAP_ABIS": true, + "DLT_GSMTAP_UM": true, + "DLT_HDLC": true, + "DLT_HHDLC": true, + "DLT_HIPPI": true, + "DLT_IBM_SN": true, + "DLT_IBM_SP": true, + "DLT_IEEE802": true, + "DLT_IEEE802_11": true, + "DLT_IEEE802_11_RADIO": true, + "DLT_IEEE802_11_RADIO_AVS": true, + "DLT_IEEE802_15_4": true, + "DLT_IEEE802_15_4_LINUX": true, + "DLT_IEEE802_15_4_NOFCS": true, + "DLT_IEEE802_15_4_NONASK_PHY": true, + "DLT_IEEE802_16_MAC_CPS": true, + "DLT_IEEE802_16_MAC_CPS_RADIO": true, + "DLT_IPFILTER": true, + "DLT_IPMB": true, + "DLT_IPMB_LINUX": true, + "DLT_IPNET": true, + "DLT_IPOIB": true, + "DLT_IPV4": true, + "DLT_IPV6": true, + "DLT_IP_OVER_FC": true, + "DLT_JUNIPER_ATM1": true, + "DLT_JUNIPER_ATM2": true, + "DLT_JUNIPER_ATM_CEMIC": true, + "DLT_JUNIPER_CHDLC": true, + "DLT_JUNIPER_ES": true, + "DLT_JUNIPER_ETHER": true, + "DLT_JUNIPER_FIBRECHANNEL": true, + "DLT_JUNIPER_FRELAY": true, + "DLT_JUNIPER_GGSN": true, + "DLT_JUNIPER_ISM": true, + "DLT_JUNIPER_MFR": true, + "DLT_JUNIPER_MLFR": true, + "DLT_JUNIPER_MLPPP": true, + "DLT_JUNIPER_MONITOR": true, + "DLT_JUNIPER_PIC_PEER": true, + "DLT_JUNIPER_PPP": true, + "DLT_JUNIPER_PPPOE": true, + "DLT_JUNIPER_PPPOE_ATM": true, + "DLT_JUNIPER_SERVICES": true, + "DLT_JUNIPER_SRX_E2E": true, + "DLT_JUNIPER_ST": true, + "DLT_JUNIPER_VP": true, + "DLT_JUNIPER_VS": true, + "DLT_LAPB_WITH_DIR": true, + "DLT_LAPD": true, + "DLT_LIN": true, + "DLT_LINUX_EVDEV": true, + "DLT_LINUX_IRDA": true, + "DLT_LINUX_LAPD": true, + "DLT_LINUX_PPP_WITHDIRECTION": true, + "DLT_LINUX_SLL": true, + "DLT_LOOP": true, + "DLT_LTALK": true, + "DLT_MATCHING_MAX": true, + "DLT_MATCHING_MIN": true, + "DLT_MFR": true, + "DLT_MOST": true, + "DLT_MPEG_2_TS": true, + "DLT_MPLS": true, + "DLT_MTP2": true, + "DLT_MTP2_WITH_PHDR": true, + "DLT_MTP3": true, + "DLT_MUX27010": true, + "DLT_NETANALYZER": true, + "DLT_NETANALYZER_TRANSPARENT": true, + "DLT_NFC_LLCP": true, + "DLT_NFLOG": true, + "DLT_NG40": true, + "DLT_NULL": true, + "DLT_PCI_EXP": true, + "DLT_PFLOG": true, + "DLT_PFSYNC": true, + "DLT_PPI": true, + "DLT_PPP": true, + "DLT_PPP_BSDOS": true, + "DLT_PPP_ETHER": true, + "DLT_PPP_PPPD": true, + "DLT_PPP_SERIAL": true, + "DLT_PPP_WITH_DIR": true, + "DLT_PPP_WITH_DIRECTION": true, + "DLT_PRISM_HEADER": true, + "DLT_PRONET": true, + "DLT_RAIF1": true, + "DLT_RAW": true, + "DLT_RAWAF_MASK": true, + "DLT_RIO": true, + "DLT_SCCP": true, + "DLT_SITA": true, + "DLT_SLIP": true, + "DLT_SLIP_BSDOS": true, + "DLT_STANAG_5066_D_PDU": true, + "DLT_SUNATM": true, + "DLT_SYMANTEC_FIREWALL": true, + "DLT_TZSP": true, + "DLT_USB": true, + "DLT_USB_LINUX": true, + "DLT_USB_LINUX_MMAPPED": true, + "DLT_USER0": true, + "DLT_USER1": true, + "DLT_USER10": true, + "DLT_USER11": true, + "DLT_USER12": true, + "DLT_USER13": true, + "DLT_USER14": true, + "DLT_USER15": true, + "DLT_USER2": true, + "DLT_USER3": true, + "DLT_USER4": true, + "DLT_USER5": true, + "DLT_USER6": true, + "DLT_USER7": true, + "DLT_USER8": true, + "DLT_USER9": true, + "DLT_WIHART": true, + "DLT_X2E_SERIAL": true, + "DLT_X2E_XORAYA": true, + "DNSMXData": true, + "DNSPTRData": true, + "DNSRecord": true, + "DNSSRVData": true, + "DNSTXTData": true, + "DNS_INFO_NO_RECORDS": true, + "DNS_TYPE_A": true, + "DNS_TYPE_A6": true, + "DNS_TYPE_AAAA": true, + "DNS_TYPE_ADDRS": true, + "DNS_TYPE_AFSDB": true, + "DNS_TYPE_ALL": true, + "DNS_TYPE_ANY": true, + "DNS_TYPE_ATMA": true, + "DNS_TYPE_AXFR": true, + "DNS_TYPE_CERT": true, + "DNS_TYPE_CNAME": true, + "DNS_TYPE_DHCID": true, + "DNS_TYPE_DNAME": true, + "DNS_TYPE_DNSKEY": true, + "DNS_TYPE_DS": true, + "DNS_TYPE_EID": true, + "DNS_TYPE_GID": true, + "DNS_TYPE_GPOS": true, + "DNS_TYPE_HINFO": true, + "DNS_TYPE_ISDN": true, + "DNS_TYPE_IXFR": true, + "DNS_TYPE_KEY": true, + "DNS_TYPE_KX": true, + "DNS_TYPE_LOC": true, + "DNS_TYPE_MAILA": true, + "DNS_TYPE_MAILB": true, + "DNS_TYPE_MB": true, + "DNS_TYPE_MD": true, + "DNS_TYPE_MF": true, + "DNS_TYPE_MG": true, + "DNS_TYPE_MINFO": true, + "DNS_TYPE_MR": true, + "DNS_TYPE_MX": true, + "DNS_TYPE_NAPTR": true, + "DNS_TYPE_NBSTAT": true, + "DNS_TYPE_NIMLOC": true, + "DNS_TYPE_NS": true, + "DNS_TYPE_NSAP": true, + "DNS_TYPE_NSAPPTR": true, + "DNS_TYPE_NSEC": true, + "DNS_TYPE_NULL": true, + "DNS_TYPE_NXT": true, + "DNS_TYPE_OPT": true, + "DNS_TYPE_PTR": true, + "DNS_TYPE_PX": true, + "DNS_TYPE_RP": true, + "DNS_TYPE_RRSIG": true, + "DNS_TYPE_RT": true, + "DNS_TYPE_SIG": true, + "DNS_TYPE_SINK": true, + "DNS_TYPE_SOA": true, + "DNS_TYPE_SRV": true, + "DNS_TYPE_TEXT": true, + "DNS_TYPE_TKEY": true, + "DNS_TYPE_TSIG": true, + "DNS_TYPE_UID": true, + "DNS_TYPE_UINFO": true, + "DNS_TYPE_UNSPEC": true, + "DNS_TYPE_WINS": true, + "DNS_TYPE_WINSR": true, + "DNS_TYPE_WKS": true, + "DNS_TYPE_X25": true, + "DT_BLK": true, + "DT_CHR": true, + "DT_DIR": true, + "DT_FIFO": true, + "DT_LNK": true, + "DT_REG": true, + "DT_SOCK": true, + "DT_UNKNOWN": true, + "DT_WHT": true, + "DUPLICATE_CLOSE_SOURCE": true, + "DUPLICATE_SAME_ACCESS": true, + "DeleteFile": true, + "DetachLsf": true, + "DeviceIoControl": true, + "Dirent": true, + "DnsNameCompare": true, + "DnsQuery": true, + "DnsRecordListFree": true, + "DnsSectionAdditional": true, + "DnsSectionAnswer": true, + "DnsSectionAuthority": true, + "DnsSectionQuestion": true, + "Dup": true, + "Dup2": true, + "Dup3": true, + "DuplicateHandle": true, + "E2BIG": true, + "EACCES": true, + "EADDRINUSE": true, + "EADDRNOTAVAIL": true, + "EADV": true, + "EAFNOSUPPORT": true, + "EAGAIN": true, + "EALREADY": true, + "EAUTH": true, + "EBADARCH": true, + "EBADE": true, + "EBADEXEC": true, + "EBADF": true, + "EBADFD": true, + "EBADMACHO": true, + "EBADMSG": true, + "EBADR": true, + "EBADRPC": true, + "EBADRQC": true, + "EBADSLT": true, + "EBFONT": true, + "EBUSY": true, + "ECANCELED": true, + "ECAPMODE": true, + "ECHILD": true, + "ECHO": true, + "ECHOCTL": true, + "ECHOE": true, + "ECHOK": true, + "ECHOKE": true, + "ECHONL": true, + "ECHOPRT": true, + "ECHRNG": true, + "ECOMM": true, + "ECONNABORTED": true, + "ECONNREFUSED": true, + "ECONNRESET": true, + "EDEADLK": true, + "EDEADLOCK": true, + "EDESTADDRREQ": true, + "EDEVERR": true, + "EDOM": true, + "EDOOFUS": true, + "EDOTDOT": true, + "EDQUOT": true, + "EEXIST": true, + "EFAULT": true, + "EFBIG": true, + "EFER_LMA": true, + "EFER_LME": true, + "EFER_NXE": true, + "EFER_SCE": true, + "EFTYPE": true, + "EHOSTDOWN": true, + "EHOSTUNREACH": true, + "EHWPOISON": true, + "EIDRM": true, + "EILSEQ": true, + "EINPROGRESS": true, + "EINTR": true, + "EINVAL": true, + "EIO": true, + "EIPSEC": true, + "EISCONN": true, + "EISDIR": true, + "EISNAM": true, + "EKEYEXPIRED": true, + "EKEYREJECTED": true, + "EKEYREVOKED": true, + "EL2HLT": true, + "EL2NSYNC": true, + "EL3HLT": true, + "EL3RST": true, + "ELAST": true, + "ELF_NGREG": true, + "ELF_PRARGSZ": true, + "ELIBACC": true, + "ELIBBAD": true, + "ELIBEXEC": true, + "ELIBMAX": true, + "ELIBSCN": true, + "ELNRNG": true, + "ELOOP": true, + "EMEDIUMTYPE": true, + "EMFILE": true, + "EMLINK": true, + "EMSGSIZE": true, + "EMT_TAGOVF": true, + "EMULTIHOP": true, + "EMUL_ENABLED": true, + "EMUL_LINUX": true, + "EMUL_LINUX32": true, + "EMUL_MAXID": true, + "EMUL_NATIVE": true, + "ENAMETOOLONG": true, + "ENAVAIL": true, + "ENDRUNDISC": true, + "ENEEDAUTH": true, + "ENETDOWN": true, + "ENETRESET": true, + "ENETUNREACH": true, + "ENFILE": true, + "ENOANO": true, + "ENOATTR": true, + "ENOBUFS": true, + "ENOCSI": true, + "ENODATA": true, + "ENODEV": true, + "ENOENT": true, + "ENOEXEC": true, + "ENOKEY": true, + "ENOLCK": true, + "ENOLINK": true, + "ENOMEDIUM": true, + "ENOMEM": true, + "ENOMSG": true, + "ENONET": true, + "ENOPKG": true, + "ENOPOLICY": true, + "ENOPROTOOPT": true, + "ENOSPC": true, + "ENOSR": true, + "ENOSTR": true, + "ENOSYS": true, + "ENOTBLK": true, + "ENOTCAPABLE": true, + "ENOTCONN": true, + "ENOTDIR": true, + "ENOTEMPTY": true, + "ENOTNAM": true, + "ENOTRECOVERABLE": true, + "ENOTSOCK": true, + "ENOTSUP": true, + "ENOTTY": true, + "ENOTUNIQ": true, + "ENXIO": true, + "EN_SW_CTL_INF": true, + "EN_SW_CTL_PREC": true, + "EN_SW_CTL_ROUND": true, + "EN_SW_DATACHAIN": true, + "EN_SW_DENORM": true, + "EN_SW_INVOP": true, + "EN_SW_OVERFLOW": true, + "EN_SW_PRECLOSS": true, + "EN_SW_UNDERFLOW": true, + "EN_SW_ZERODIV": true, + "EOPNOTSUPP": true, + "EOVERFLOW": true, + "EOWNERDEAD": true, + "EPERM": true, + "EPFNOSUPPORT": true, + "EPIPE": true, + "EPOLLERR": true, + "EPOLLET": true, + "EPOLLHUP": true, + "EPOLLIN": true, + "EPOLLMSG": true, + "EPOLLONESHOT": true, + "EPOLLOUT": true, + "EPOLLPRI": true, + "EPOLLRDBAND": true, + "EPOLLRDHUP": true, + "EPOLLRDNORM": true, + "EPOLLWRBAND": true, + "EPOLLWRNORM": true, + "EPOLL_CLOEXEC": true, + "EPOLL_CTL_ADD": true, + "EPOLL_CTL_DEL": true, + "EPOLL_CTL_MOD": true, + "EPOLL_NONBLOCK": true, + "EPROCLIM": true, + "EPROCUNAVAIL": true, + "EPROGMISMATCH": true, + "EPROGUNAVAIL": true, + "EPROTO": true, + "EPROTONOSUPPORT": true, + "EPROTOTYPE": true, + "EPWROFF": true, + "ERANGE": true, + "EREMCHG": true, + "EREMOTE": true, + "EREMOTEIO": true, + "ERESTART": true, + "ERFKILL": true, + "EROFS": true, + "ERPCMISMATCH": true, + "ERROR_ACCESS_DENIED": true, + "ERROR_ALREADY_EXISTS": true, + "ERROR_BROKEN_PIPE": true, + "ERROR_BUFFER_OVERFLOW": true, + "ERROR_DIR_NOT_EMPTY": true, + "ERROR_ENVVAR_NOT_FOUND": true, + "ERROR_FILE_EXISTS": true, + "ERROR_FILE_NOT_FOUND": true, + "ERROR_HANDLE_EOF": true, + "ERROR_INSUFFICIENT_BUFFER": true, + "ERROR_IO_PENDING": true, + "ERROR_MOD_NOT_FOUND": true, + "ERROR_MORE_DATA": true, + "ERROR_NETNAME_DELETED": true, + "ERROR_NOT_FOUND": true, + "ERROR_NO_MORE_FILES": true, + "ERROR_OPERATION_ABORTED": true, + "ERROR_PATH_NOT_FOUND": true, + "ERROR_PRIVILEGE_NOT_HELD": true, + "ERROR_PROC_NOT_FOUND": true, + "ESHLIBVERS": true, + "ESHUTDOWN": true, + "ESOCKTNOSUPPORT": true, + "ESPIPE": true, + "ESRCH": true, + "ESRMNT": true, + "ESTALE": true, + "ESTRPIPE": true, + "ETHERCAP_JUMBO_MTU": true, + "ETHERCAP_VLAN_HWTAGGING": true, + "ETHERCAP_VLAN_MTU": true, + "ETHERMIN": true, + "ETHERMTU": true, + "ETHERMTU_JUMBO": true, + "ETHERTYPE_8023": true, + "ETHERTYPE_AARP": true, + "ETHERTYPE_ACCTON": true, + "ETHERTYPE_AEONIC": true, + "ETHERTYPE_ALPHA": true, + "ETHERTYPE_AMBER": true, + "ETHERTYPE_AMOEBA": true, + "ETHERTYPE_AOE": true, + "ETHERTYPE_APOLLO": true, + "ETHERTYPE_APOLLODOMAIN": true, + "ETHERTYPE_APPLETALK": true, + "ETHERTYPE_APPLITEK": true, + "ETHERTYPE_ARGONAUT": true, + "ETHERTYPE_ARP": true, + "ETHERTYPE_AT": true, + "ETHERTYPE_ATALK": true, + "ETHERTYPE_ATOMIC": true, + "ETHERTYPE_ATT": true, + "ETHERTYPE_ATTSTANFORD": true, + "ETHERTYPE_AUTOPHON": true, + "ETHERTYPE_AXIS": true, + "ETHERTYPE_BCLOOP": true, + "ETHERTYPE_BOFL": true, + "ETHERTYPE_CABLETRON": true, + "ETHERTYPE_CHAOS": true, + "ETHERTYPE_COMDESIGN": true, + "ETHERTYPE_COMPUGRAPHIC": true, + "ETHERTYPE_COUNTERPOINT": true, + "ETHERTYPE_CRONUS": true, + "ETHERTYPE_CRONUSVLN": true, + "ETHERTYPE_DCA": true, + "ETHERTYPE_DDE": true, + "ETHERTYPE_DEBNI": true, + "ETHERTYPE_DECAM": true, + "ETHERTYPE_DECCUST": true, + "ETHERTYPE_DECDIAG": true, + "ETHERTYPE_DECDNS": true, + "ETHERTYPE_DECDTS": true, + "ETHERTYPE_DECEXPER": true, + "ETHERTYPE_DECLAST": true, + "ETHERTYPE_DECLTM": true, + "ETHERTYPE_DECMUMPS": true, + "ETHERTYPE_DECNETBIOS": true, + "ETHERTYPE_DELTACON": true, + "ETHERTYPE_DIDDLE": true, + "ETHERTYPE_DLOG1": true, + "ETHERTYPE_DLOG2": true, + "ETHERTYPE_DN": true, + "ETHERTYPE_DOGFIGHT": true, + "ETHERTYPE_DSMD": true, + "ETHERTYPE_ECMA": true, + "ETHERTYPE_ENCRYPT": true, + "ETHERTYPE_ES": true, + "ETHERTYPE_EXCELAN": true, + "ETHERTYPE_EXPERDATA": true, + "ETHERTYPE_FLIP": true, + "ETHERTYPE_FLOWCONTROL": true, + "ETHERTYPE_FRARP": true, + "ETHERTYPE_GENDYN": true, + "ETHERTYPE_HAYES": true, + "ETHERTYPE_HIPPI_FP": true, + "ETHERTYPE_HITACHI": true, + "ETHERTYPE_HP": true, + "ETHERTYPE_IEEEPUP": true, + "ETHERTYPE_IEEEPUPAT": true, + "ETHERTYPE_IMLBL": true, + "ETHERTYPE_IMLBLDIAG": true, + "ETHERTYPE_IP": true, + "ETHERTYPE_IPAS": true, + "ETHERTYPE_IPV6": true, + "ETHERTYPE_IPX": true, + "ETHERTYPE_IPXNEW": true, + "ETHERTYPE_KALPANA": true, + "ETHERTYPE_LANBRIDGE": true, + "ETHERTYPE_LANPROBE": true, + "ETHERTYPE_LAT": true, + "ETHERTYPE_LBACK": true, + "ETHERTYPE_LITTLE": true, + "ETHERTYPE_LLDP": true, + "ETHERTYPE_LOGICRAFT": true, + "ETHERTYPE_LOOPBACK": true, + "ETHERTYPE_MATRA": true, + "ETHERTYPE_MAX": true, + "ETHERTYPE_MERIT": true, + "ETHERTYPE_MICP": true, + "ETHERTYPE_MOPDL": true, + "ETHERTYPE_MOPRC": true, + "ETHERTYPE_MOTOROLA": true, + "ETHERTYPE_MPLS": true, + "ETHERTYPE_MPLS_MCAST": true, + "ETHERTYPE_MUMPS": true, + "ETHERTYPE_NBPCC": true, + "ETHERTYPE_NBPCLAIM": true, + "ETHERTYPE_NBPCLREQ": true, + "ETHERTYPE_NBPCLRSP": true, + "ETHERTYPE_NBPCREQ": true, + "ETHERTYPE_NBPCRSP": true, + "ETHERTYPE_NBPDG": true, + "ETHERTYPE_NBPDGB": true, + "ETHERTYPE_NBPDLTE": true, + "ETHERTYPE_NBPRAR": true, + "ETHERTYPE_NBPRAS": true, + "ETHERTYPE_NBPRST": true, + "ETHERTYPE_NBPSCD": true, + "ETHERTYPE_NBPVCD": true, + "ETHERTYPE_NBS": true, + "ETHERTYPE_NCD": true, + "ETHERTYPE_NESTAR": true, + "ETHERTYPE_NETBEUI": true, + "ETHERTYPE_NOVELL": true, + "ETHERTYPE_NS": true, + "ETHERTYPE_NSAT": true, + "ETHERTYPE_NSCOMPAT": true, + "ETHERTYPE_NTRAILER": true, + "ETHERTYPE_OS9": true, + "ETHERTYPE_OS9NET": true, + "ETHERTYPE_PACER": true, + "ETHERTYPE_PAE": true, + "ETHERTYPE_PCS": true, + "ETHERTYPE_PLANNING": true, + "ETHERTYPE_PPP": true, + "ETHERTYPE_PPPOE": true, + "ETHERTYPE_PPPOEDISC": true, + "ETHERTYPE_PRIMENTS": true, + "ETHERTYPE_PUP": true, + "ETHERTYPE_PUPAT": true, + "ETHERTYPE_QINQ": true, + "ETHERTYPE_RACAL": true, + "ETHERTYPE_RATIONAL": true, + "ETHERTYPE_RAWFR": true, + "ETHERTYPE_RCL": true, + "ETHERTYPE_RDP": true, + "ETHERTYPE_RETIX": true, + "ETHERTYPE_REVARP": true, + "ETHERTYPE_SCA": true, + "ETHERTYPE_SECTRA": true, + "ETHERTYPE_SECUREDATA": true, + "ETHERTYPE_SGITW": true, + "ETHERTYPE_SG_BOUNCE": true, + "ETHERTYPE_SG_DIAG": true, + "ETHERTYPE_SG_NETGAMES": true, + "ETHERTYPE_SG_RESV": true, + "ETHERTYPE_SIMNET": true, + "ETHERTYPE_SLOW": true, + "ETHERTYPE_SLOWPROTOCOLS": true, + "ETHERTYPE_SNA": true, + "ETHERTYPE_SNMP": true, + "ETHERTYPE_SONIX": true, + "ETHERTYPE_SPIDER": true, + "ETHERTYPE_SPRITE": true, + "ETHERTYPE_STP": true, + "ETHERTYPE_TALARIS": true, + "ETHERTYPE_TALARISMC": true, + "ETHERTYPE_TCPCOMP": true, + "ETHERTYPE_TCPSM": true, + "ETHERTYPE_TEC": true, + "ETHERTYPE_TIGAN": true, + "ETHERTYPE_TRAIL": true, + "ETHERTYPE_TRANSETHER": true, + "ETHERTYPE_TYMSHARE": true, + "ETHERTYPE_UBBST": true, + "ETHERTYPE_UBDEBUG": true, + "ETHERTYPE_UBDIAGLOOP": true, + "ETHERTYPE_UBDL": true, + "ETHERTYPE_UBNIU": true, + "ETHERTYPE_UBNMC": true, + "ETHERTYPE_VALID": true, + "ETHERTYPE_VARIAN": true, + "ETHERTYPE_VAXELN": true, + "ETHERTYPE_VEECO": true, + "ETHERTYPE_VEXP": true, + "ETHERTYPE_VGLAB": true, + "ETHERTYPE_VINES": true, + "ETHERTYPE_VINESECHO": true, + "ETHERTYPE_VINESLOOP": true, + "ETHERTYPE_VITAL": true, + "ETHERTYPE_VLAN": true, + "ETHERTYPE_VLTLMAN": true, + "ETHERTYPE_VPROD": true, + "ETHERTYPE_VURESERVED": true, + "ETHERTYPE_WATERLOO": true, + "ETHERTYPE_WELLFLEET": true, + "ETHERTYPE_X25": true, + "ETHERTYPE_X75": true, + "ETHERTYPE_XNSSM": true, + "ETHERTYPE_XTP": true, + "ETHER_ADDR_LEN": true, + "ETHER_ALIGN": true, + "ETHER_CRC_LEN": true, + "ETHER_CRC_POLY_BE": true, + "ETHER_CRC_POLY_LE": true, + "ETHER_HDR_LEN": true, + "ETHER_MAX_DIX_LEN": true, + "ETHER_MAX_LEN": true, + "ETHER_MAX_LEN_JUMBO": true, + "ETHER_MIN_LEN": true, + "ETHER_PPPOE_ENCAP_LEN": true, + "ETHER_TYPE_LEN": true, + "ETHER_VLAN_ENCAP_LEN": true, + "ETH_P_1588": true, + "ETH_P_8021Q": true, + "ETH_P_802_2": true, + "ETH_P_802_3": true, + "ETH_P_AARP": true, + "ETH_P_ALL": true, + "ETH_P_AOE": true, + "ETH_P_ARCNET": true, + "ETH_P_ARP": true, + "ETH_P_ATALK": true, + "ETH_P_ATMFATE": true, + "ETH_P_ATMMPOA": true, + "ETH_P_AX25": true, + "ETH_P_BPQ": true, + "ETH_P_CAIF": true, + "ETH_P_CAN": true, + "ETH_P_CONTROL": true, + "ETH_P_CUST": true, + "ETH_P_DDCMP": true, + "ETH_P_DEC": true, + "ETH_P_DIAG": true, + "ETH_P_DNA_DL": true, + "ETH_P_DNA_RC": true, + "ETH_P_DNA_RT": true, + "ETH_P_DSA": true, + "ETH_P_ECONET": true, + "ETH_P_EDSA": true, + "ETH_P_FCOE": true, + "ETH_P_FIP": true, + "ETH_P_HDLC": true, + "ETH_P_IEEE802154": true, + "ETH_P_IEEEPUP": true, + "ETH_P_IEEEPUPAT": true, + "ETH_P_IP": true, + "ETH_P_IPV6": true, + "ETH_P_IPX": true, + "ETH_P_IRDA": true, + "ETH_P_LAT": true, + "ETH_P_LINK_CTL": true, + "ETH_P_LOCALTALK": true, + "ETH_P_LOOP": true, + "ETH_P_MOBITEX": true, + "ETH_P_MPLS_MC": true, + "ETH_P_MPLS_UC": true, + "ETH_P_PAE": true, + "ETH_P_PAUSE": true, + "ETH_P_PHONET": true, + "ETH_P_PPPTALK": true, + "ETH_P_PPP_DISC": true, + "ETH_P_PPP_MP": true, + "ETH_P_PPP_SES": true, + "ETH_P_PUP": true, + "ETH_P_PUPAT": true, + "ETH_P_RARP": true, + "ETH_P_SCA": true, + "ETH_P_SLOW": true, + "ETH_P_SNAP": true, + "ETH_P_TEB": true, + "ETH_P_TIPC": true, + "ETH_P_TRAILER": true, + "ETH_P_TR_802_2": true, + "ETH_P_WAN_PPP": true, + "ETH_P_WCCP": true, + "ETH_P_X25": true, + "ETIME": true, + "ETIMEDOUT": true, + "ETOOMANYREFS": true, + "ETXTBSY": true, + "EUCLEAN": true, + "EUNATCH": true, + "EUSERS": true, + "EVFILT_AIO": true, + "EVFILT_FS": true, + "EVFILT_LIO": true, + "EVFILT_MACHPORT": true, + "EVFILT_PROC": true, + "EVFILT_READ": true, + "EVFILT_SIGNAL": true, + "EVFILT_SYSCOUNT": true, + "EVFILT_THREADMARKER": true, + "EVFILT_TIMER": true, + "EVFILT_USER": true, + "EVFILT_VM": true, + "EVFILT_VNODE": true, + "EVFILT_WRITE": true, + "EV_ADD": true, + "EV_CLEAR": true, + "EV_DELETE": true, + "EV_DISABLE": true, + "EV_DISPATCH": true, + "EV_DROP": true, + "EV_ENABLE": true, + "EV_EOF": true, + "EV_ERROR": true, + "EV_FLAG0": true, + "EV_FLAG1": true, + "EV_ONESHOT": true, + "EV_OOBAND": true, + "EV_POLL": true, + "EV_RECEIPT": true, + "EV_SYSFLAGS": true, + "EWINDOWS": true, + "EWOULDBLOCK": true, + "EXDEV": true, + "EXFULL": true, + "EXTA": true, + "EXTB": true, + "EXTPROC": true, + "Environ": true, + "EpollCreate": true, + "EpollCreate1": true, + "EpollCtl": true, + "EpollEvent": true, + "EpollWait": true, + "Errno": true, + "EscapeArg": true, + "Exchangedata": true, + "Exec": true, + "Exit": true, + "ExitProcess": true, + "FD_CLOEXEC": true, + "FD_SETSIZE": true, + "FILE_ACTION_ADDED": true, + "FILE_ACTION_MODIFIED": true, + "FILE_ACTION_REMOVED": true, + "FILE_ACTION_RENAMED_NEW_NAME": true, + "FILE_ACTION_RENAMED_OLD_NAME": true, + "FILE_APPEND_DATA": true, + "FILE_ATTRIBUTE_ARCHIVE": true, + "FILE_ATTRIBUTE_DIRECTORY": true, + "FILE_ATTRIBUTE_HIDDEN": true, + "FILE_ATTRIBUTE_NORMAL": true, + "FILE_ATTRIBUTE_READONLY": true, + "FILE_ATTRIBUTE_REPARSE_POINT": true, + "FILE_ATTRIBUTE_SYSTEM": true, + "FILE_BEGIN": true, + "FILE_CURRENT": true, + "FILE_END": true, + "FILE_FLAG_BACKUP_SEMANTICS": true, + "FILE_FLAG_OPEN_REPARSE_POINT": true, + "FILE_FLAG_OVERLAPPED": true, + "FILE_LIST_DIRECTORY": true, + "FILE_MAP_COPY": true, + "FILE_MAP_EXECUTE": true, + "FILE_MAP_READ": true, + "FILE_MAP_WRITE": true, + "FILE_NOTIFY_CHANGE_ATTRIBUTES": true, + "FILE_NOTIFY_CHANGE_CREATION": true, + "FILE_NOTIFY_CHANGE_DIR_NAME": true, + "FILE_NOTIFY_CHANGE_FILE_NAME": true, + "FILE_NOTIFY_CHANGE_LAST_ACCESS": true, + "FILE_NOTIFY_CHANGE_LAST_WRITE": true, + "FILE_NOTIFY_CHANGE_SIZE": true, + "FILE_SHARE_DELETE": true, + "FILE_SHARE_READ": true, + "FILE_SHARE_WRITE": true, + "FILE_SKIP_COMPLETION_PORT_ON_SUCCESS": true, + "FILE_SKIP_SET_EVENT_ON_HANDLE": true, + "FILE_TYPE_CHAR": true, + "FILE_TYPE_DISK": true, + "FILE_TYPE_PIPE": true, + "FILE_TYPE_REMOTE": true, + "FILE_TYPE_UNKNOWN": true, + "FILE_WRITE_ATTRIBUTES": true, + "FLUSHO": true, + "FORMAT_MESSAGE_ALLOCATE_BUFFER": true, + "FORMAT_MESSAGE_ARGUMENT_ARRAY": true, + "FORMAT_MESSAGE_FROM_HMODULE": true, + "FORMAT_MESSAGE_FROM_STRING": true, + "FORMAT_MESSAGE_FROM_SYSTEM": true, + "FORMAT_MESSAGE_IGNORE_INSERTS": true, + "FORMAT_MESSAGE_MAX_WIDTH_MASK": true, + "FSCTL_GET_REPARSE_POINT": true, + "F_ADDFILESIGS": true, + "F_ADDSIGS": true, + "F_ALLOCATEALL": true, + "F_ALLOCATECONTIG": true, + "F_CANCEL": true, + "F_CHKCLEAN": true, + "F_CLOSEM": true, + "F_DUP2FD": true, + "F_DUP2FD_CLOEXEC": true, + "F_DUPFD": true, + "F_DUPFD_CLOEXEC": true, + "F_EXLCK": true, + "F_FLUSH_DATA": true, + "F_FREEZE_FS": true, + "F_FSCTL": true, + "F_FSDIRMASK": true, + "F_FSIN": true, + "F_FSINOUT": true, + "F_FSOUT": true, + "F_FSPRIV": true, + "F_FSVOID": true, + "F_FULLFSYNC": true, + "F_GETFD": true, + "F_GETFL": true, + "F_GETLEASE": true, + "F_GETLK": true, + "F_GETLK64": true, + "F_GETLKPID": true, + "F_GETNOSIGPIPE": true, + "F_GETOWN": true, + "F_GETOWN_EX": true, + "F_GETPATH": true, + "F_GETPATH_MTMINFO": true, + "F_GETPIPE_SZ": true, + "F_GETPROTECTIONCLASS": true, + "F_GETSIG": true, + "F_GLOBAL_NOCACHE": true, + "F_LOCK": true, + "F_LOG2PHYS": true, + "F_LOG2PHYS_EXT": true, + "F_MARKDEPENDENCY": true, + "F_MAXFD": true, + "F_NOCACHE": true, + "F_NODIRECT": true, + "F_NOTIFY": true, + "F_OGETLK": true, + "F_OK": true, + "F_OSETLK": true, + "F_OSETLKW": true, + "F_PARAM_MASK": true, + "F_PARAM_MAX": true, + "F_PATHPKG_CHECK": true, + "F_PEOFPOSMODE": true, + "F_PREALLOCATE": true, + "F_RDADVISE": true, + "F_RDAHEAD": true, + "F_RDLCK": true, + "F_READAHEAD": true, + "F_READBOOTSTRAP": true, + "F_SETBACKINGSTORE": true, + "F_SETFD": true, + "F_SETFL": true, + "F_SETLEASE": true, + "F_SETLK": true, + "F_SETLK64": true, + "F_SETLKW": true, + "F_SETLKW64": true, + "F_SETLK_REMOTE": true, + "F_SETNOSIGPIPE": true, + "F_SETOWN": true, + "F_SETOWN_EX": true, + "F_SETPIPE_SZ": true, + "F_SETPROTECTIONCLASS": true, + "F_SETSIG": true, + "F_SETSIZE": true, + "F_SHLCK": true, + "F_TEST": true, + "F_THAW_FS": true, + "F_TLOCK": true, + "F_ULOCK": true, + "F_UNLCK": true, + "F_UNLCKSYS": true, + "F_VOLPOSMODE": true, + "F_WRITEBOOTSTRAP": true, + "F_WRLCK": true, + "Faccessat": true, + "Fallocate": true, + "Fbootstraptransfer_t": true, + "Fchdir": true, + "Fchflags": true, + "Fchmod": true, + "Fchmodat": true, + "Fchown": true, + "Fchownat": true, + "FcntlFlock": true, + "FdSet": true, + "Fdatasync": true, + "FileNotifyInformation": true, + "Filetime": true, + "FindClose": true, + "FindFirstFile": true, + "FindNextFile": true, + "Flock": true, + "Flock_t": true, + "FlushBpf": true, + "FlushFileBuffers": true, + "FlushViewOfFile": true, + "ForkExec": true, + "ForkLock": true, + "FormatMessage": true, + "Fpathconf": true, + "FreeAddrInfoW": true, + "FreeEnvironmentStrings": true, + "FreeLibrary": true, + "Fsid": true, + "Fstat": true, + "Fstatat": true, + "Fstatfs": true, + "Fstore_t": true, + "Fsync": true, + "Ftruncate": true, + "FullPath": true, + "Futimes": true, + "Futimesat": true, + "GENERIC_ALL": true, + "GENERIC_EXECUTE": true, + "GENERIC_READ": true, + "GENERIC_WRITE": true, + "GUID": true, + "GetAcceptExSockaddrs": true, + "GetAdaptersInfo": true, + "GetAddrInfoW": true, + "GetCommandLine": true, + "GetComputerName": true, + "GetConsoleMode": true, + "GetCurrentDirectory": true, + "GetCurrentProcess": true, + "GetEnvironmentStrings": true, + "GetEnvironmentVariable": true, + "GetExitCodeProcess": true, + "GetFileAttributes": true, + "GetFileAttributesEx": true, + "GetFileExInfoStandard": true, + "GetFileExMaxInfoLevel": true, + "GetFileInformationByHandle": true, + "GetFileType": true, + "GetFullPathName": true, + "GetHostByName": true, + "GetIfEntry": true, + "GetLastError": true, + "GetLengthSid": true, + "GetLongPathName": true, + "GetProcAddress": true, + "GetProcessTimes": true, + "GetProtoByName": true, + "GetQueuedCompletionStatus": true, + "GetServByName": true, + "GetShortPathName": true, + "GetStartupInfo": true, + "GetStdHandle": true, + "GetSystemTimeAsFileTime": true, + "GetTempPath": true, + "GetTimeZoneInformation": true, + "GetTokenInformation": true, + "GetUserNameEx": true, + "GetUserProfileDirectory": true, + "GetVersion": true, + "Getcwd": true, + "Getdents": true, + "Getdirentries": true, + "Getdtablesize": true, + "Getegid": true, + "Getenv": true, + "Geteuid": true, + "Getfsstat": true, + "Getgid": true, + "Getgroups": true, + "Getpagesize": true, + "Getpeername": true, + "Getpgid": true, + "Getpgrp": true, + "Getpid": true, + "Getppid": true, + "Getpriority": true, + "Getrlimit": true, + "Getrusage": true, + "Getsid": true, + "Getsockname": true, + "Getsockopt": true, + "GetsockoptByte": true, + "GetsockoptICMPv6Filter": true, + "GetsockoptIPMreq": true, + "GetsockoptIPMreqn": true, + "GetsockoptIPv6MTUInfo": true, + "GetsockoptIPv6Mreq": true, + "GetsockoptInet4Addr": true, + "GetsockoptInt": true, + "GetsockoptUcred": true, + "Gettid": true, + "Gettimeofday": true, + "Getuid": true, + "Getwd": true, + "Getxattr": true, + "HANDLE_FLAG_INHERIT": true, + "HKEY_CLASSES_ROOT": true, + "HKEY_CURRENT_CONFIG": true, + "HKEY_CURRENT_USER": true, + "HKEY_DYN_DATA": true, + "HKEY_LOCAL_MACHINE": true, + "HKEY_PERFORMANCE_DATA": true, + "HKEY_USERS": true, + "HUPCL": true, + "Handle": true, + "Hostent": true, + "ICANON": true, + "ICMP6_FILTER": true, + "ICMPV6_FILTER": true, + "ICMPv6Filter": true, + "ICRNL": true, + "IEXTEN": true, + "IFAN_ARRIVAL": true, + "IFAN_DEPARTURE": true, + "IFA_ADDRESS": true, + "IFA_ANYCAST": true, + "IFA_BROADCAST": true, + "IFA_CACHEINFO": true, + "IFA_F_DADFAILED": true, + "IFA_F_DEPRECATED": true, + "IFA_F_HOMEADDRESS": true, + "IFA_F_NODAD": true, + "IFA_F_OPTIMISTIC": true, + "IFA_F_PERMANENT": true, + "IFA_F_SECONDARY": true, + "IFA_F_TEMPORARY": true, + "IFA_F_TENTATIVE": true, + "IFA_LABEL": true, + "IFA_LOCAL": true, + "IFA_MAX": true, + "IFA_MULTICAST": true, + "IFA_ROUTE": true, + "IFA_UNSPEC": true, + "IFF_ALLMULTI": true, + "IFF_ALTPHYS": true, + "IFF_AUTOMEDIA": true, + "IFF_BROADCAST": true, + "IFF_CANTCHANGE": true, + "IFF_CANTCONFIG": true, + "IFF_DEBUG": true, + "IFF_DRV_OACTIVE": true, + "IFF_DRV_RUNNING": true, + "IFF_DYING": true, + "IFF_DYNAMIC": true, + "IFF_LINK0": true, + "IFF_LINK1": true, + "IFF_LINK2": true, + "IFF_LOOPBACK": true, + "IFF_MASTER": true, + "IFF_MONITOR": true, + "IFF_MULTICAST": true, + "IFF_NOARP": true, + "IFF_NOTRAILERS": true, + "IFF_NO_PI": true, + "IFF_OACTIVE": true, + "IFF_ONE_QUEUE": true, + "IFF_POINTOPOINT": true, + "IFF_POINTTOPOINT": true, + "IFF_PORTSEL": true, + "IFF_PPROMISC": true, + "IFF_PROMISC": true, + "IFF_RENAMING": true, + "IFF_RUNNING": true, + "IFF_SIMPLEX": true, + "IFF_SLAVE": true, + "IFF_SMART": true, + "IFF_STATICARP": true, + "IFF_TAP": true, + "IFF_TUN": true, + "IFF_TUN_EXCL": true, + "IFF_UP": true, + "IFF_VNET_HDR": true, + "IFLA_ADDRESS": true, + "IFLA_BROADCAST": true, + "IFLA_COST": true, + "IFLA_IFALIAS": true, + "IFLA_IFNAME": true, + "IFLA_LINK": true, + "IFLA_LINKINFO": true, + "IFLA_LINKMODE": true, + "IFLA_MAP": true, + "IFLA_MASTER": true, + "IFLA_MAX": true, + "IFLA_MTU": true, + "IFLA_NET_NS_PID": true, + "IFLA_OPERSTATE": true, + "IFLA_PRIORITY": true, + "IFLA_PROTINFO": true, + "IFLA_QDISC": true, + "IFLA_STATS": true, + "IFLA_TXQLEN": true, + "IFLA_UNSPEC": true, + "IFLA_WEIGHT": true, + "IFLA_WIRELESS": true, + "IFNAMSIZ": true, + "IFT_1822": true, + "IFT_A12MPPSWITCH": true, + "IFT_AAL2": true, + "IFT_AAL5": true, + "IFT_ADSL": true, + "IFT_AFLANE8023": true, + "IFT_AFLANE8025": true, + "IFT_ARAP": true, + "IFT_ARCNET": true, + "IFT_ARCNETPLUS": true, + "IFT_ASYNC": true, + "IFT_ATM": true, + "IFT_ATMDXI": true, + "IFT_ATMFUNI": true, + "IFT_ATMIMA": true, + "IFT_ATMLOGICAL": true, + "IFT_ATMRADIO": true, + "IFT_ATMSUBINTERFACE": true, + "IFT_ATMVCIENDPT": true, + "IFT_ATMVIRTUAL": true, + "IFT_BGPPOLICYACCOUNTING": true, + "IFT_BLUETOOTH": true, + "IFT_BRIDGE": true, + "IFT_BSC": true, + "IFT_CARP": true, + "IFT_CCTEMUL": true, + "IFT_CELLULAR": true, + "IFT_CEPT": true, + "IFT_CES": true, + "IFT_CHANNEL": true, + "IFT_CNR": true, + "IFT_COFFEE": true, + "IFT_COMPOSITELINK": true, + "IFT_DCN": true, + "IFT_DIGITALPOWERLINE": true, + "IFT_DIGITALWRAPPEROVERHEADCHANNEL": true, + "IFT_DLSW": true, + "IFT_DOCSCABLEDOWNSTREAM": true, + "IFT_DOCSCABLEMACLAYER": true, + "IFT_DOCSCABLEUPSTREAM": true, + "IFT_DOCSCABLEUPSTREAMCHANNEL": true, + "IFT_DS0": true, + "IFT_DS0BUNDLE": true, + "IFT_DS1FDL": true, + "IFT_DS3": true, + "IFT_DTM": true, + "IFT_DUMMY": true, + "IFT_DVBASILN": true, + "IFT_DVBASIOUT": true, + "IFT_DVBRCCDOWNSTREAM": true, + "IFT_DVBRCCMACLAYER": true, + "IFT_DVBRCCUPSTREAM": true, + "IFT_ECONET": true, + "IFT_ENC": true, + "IFT_EON": true, + "IFT_EPLRS": true, + "IFT_ESCON": true, + "IFT_ETHER": true, + "IFT_FAITH": true, + "IFT_FAST": true, + "IFT_FASTETHER": true, + "IFT_FASTETHERFX": true, + "IFT_FDDI": true, + "IFT_FIBRECHANNEL": true, + "IFT_FRAMERELAYINTERCONNECT": true, + "IFT_FRAMERELAYMPI": true, + "IFT_FRDLCIENDPT": true, + "IFT_FRELAY": true, + "IFT_FRELAYDCE": true, + "IFT_FRF16MFRBUNDLE": true, + "IFT_FRFORWARD": true, + "IFT_G703AT2MB": true, + "IFT_G703AT64K": true, + "IFT_GIF": true, + "IFT_GIGABITETHERNET": true, + "IFT_GR303IDT": true, + "IFT_GR303RDT": true, + "IFT_H323GATEKEEPER": true, + "IFT_H323PROXY": true, + "IFT_HDH1822": true, + "IFT_HDLC": true, + "IFT_HDSL2": true, + "IFT_HIPERLAN2": true, + "IFT_HIPPI": true, + "IFT_HIPPIINTERFACE": true, + "IFT_HOSTPAD": true, + "IFT_HSSI": true, + "IFT_HY": true, + "IFT_IBM370PARCHAN": true, + "IFT_IDSL": true, + "IFT_IEEE1394": true, + "IFT_IEEE80211": true, + "IFT_IEEE80212": true, + "IFT_IEEE8023ADLAG": true, + "IFT_IFGSN": true, + "IFT_IMT": true, + "IFT_INFINIBAND": true, + "IFT_INTERLEAVE": true, + "IFT_IP": true, + "IFT_IPFORWARD": true, + "IFT_IPOVERATM": true, + "IFT_IPOVERCDLC": true, + "IFT_IPOVERCLAW": true, + "IFT_IPSWITCH": true, + "IFT_IPXIP": true, + "IFT_ISDN": true, + "IFT_ISDNBASIC": true, + "IFT_ISDNPRIMARY": true, + "IFT_ISDNS": true, + "IFT_ISDNU": true, + "IFT_ISO88022LLC": true, + "IFT_ISO88023": true, + "IFT_ISO88024": true, + "IFT_ISO88025": true, + "IFT_ISO88025CRFPINT": true, + "IFT_ISO88025DTR": true, + "IFT_ISO88025FIBER": true, + "IFT_ISO88026": true, + "IFT_ISUP": true, + "IFT_L2VLAN": true, + "IFT_L3IPVLAN": true, + "IFT_L3IPXVLAN": true, + "IFT_LAPB": true, + "IFT_LAPD": true, + "IFT_LAPF": true, + "IFT_LINEGROUP": true, + "IFT_LOCALTALK": true, + "IFT_LOOP": true, + "IFT_MEDIAMAILOVERIP": true, + "IFT_MFSIGLINK": true, + "IFT_MIOX25": true, + "IFT_MODEM": true, + "IFT_MPC": true, + "IFT_MPLS": true, + "IFT_MPLSTUNNEL": true, + "IFT_MSDSL": true, + "IFT_MVL": true, + "IFT_MYRINET": true, + "IFT_NFAS": true, + "IFT_NSIP": true, + "IFT_OPTICALCHANNEL": true, + "IFT_OPTICALTRANSPORT": true, + "IFT_OTHER": true, + "IFT_P10": true, + "IFT_P80": true, + "IFT_PARA": true, + "IFT_PDP": true, + "IFT_PFLOG": true, + "IFT_PFLOW": true, + "IFT_PFSYNC": true, + "IFT_PLC": true, + "IFT_PON155": true, + "IFT_PON622": true, + "IFT_POS": true, + "IFT_PPP": true, + "IFT_PPPMULTILINKBUNDLE": true, + "IFT_PROPATM": true, + "IFT_PROPBWAP2MP": true, + "IFT_PROPCNLS": true, + "IFT_PROPDOCSWIRELESSDOWNSTREAM": true, + "IFT_PROPDOCSWIRELESSMACLAYER": true, + "IFT_PROPDOCSWIRELESSUPSTREAM": true, + "IFT_PROPMUX": true, + "IFT_PROPVIRTUAL": true, + "IFT_PROPWIRELESSP2P": true, + "IFT_PTPSERIAL": true, + "IFT_PVC": true, + "IFT_Q2931": true, + "IFT_QLLC": true, + "IFT_RADIOMAC": true, + "IFT_RADSL": true, + "IFT_REACHDSL": true, + "IFT_RFC1483": true, + "IFT_RS232": true, + "IFT_RSRB": true, + "IFT_SDLC": true, + "IFT_SDSL": true, + "IFT_SHDSL": true, + "IFT_SIP": true, + "IFT_SIPSIG": true, + "IFT_SIPTG": true, + "IFT_SLIP": true, + "IFT_SMDSDXI": true, + "IFT_SMDSICIP": true, + "IFT_SONET": true, + "IFT_SONETOVERHEADCHANNEL": true, + "IFT_SONETPATH": true, + "IFT_SONETVT": true, + "IFT_SRP": true, + "IFT_SS7SIGLINK": true, + "IFT_STACKTOSTACK": true, + "IFT_STARLAN": true, + "IFT_STF": true, + "IFT_T1": true, + "IFT_TDLC": true, + "IFT_TELINK": true, + "IFT_TERMPAD": true, + "IFT_TR008": true, + "IFT_TRANSPHDLC": true, + "IFT_TUNNEL": true, + "IFT_ULTRA": true, + "IFT_USB": true, + "IFT_V11": true, + "IFT_V35": true, + "IFT_V36": true, + "IFT_V37": true, + "IFT_VDSL": true, + "IFT_VIRTUALIPADDRESS": true, + "IFT_VIRTUALTG": true, + "IFT_VOICEDID": true, + "IFT_VOICEEM": true, + "IFT_VOICEEMFGD": true, + "IFT_VOICEENCAP": true, + "IFT_VOICEFGDEANA": true, + "IFT_VOICEFXO": true, + "IFT_VOICEFXS": true, + "IFT_VOICEOVERATM": true, + "IFT_VOICEOVERCABLE": true, + "IFT_VOICEOVERFRAMERELAY": true, + "IFT_VOICEOVERIP": true, + "IFT_X213": true, + "IFT_X25": true, + "IFT_X25DDN": true, + "IFT_X25HUNTGROUP": true, + "IFT_X25MLP": true, + "IFT_X25PLE": true, + "IFT_XETHER": true, + "IGNBRK": true, + "IGNCR": true, + "IGNORE": true, + "IGNPAR": true, + "IMAXBEL": true, + "INFINITE": true, + "INLCR": true, + "INPCK": true, + "INVALID_FILE_ATTRIBUTES": true, + "IN_ACCESS": true, + "IN_ALL_EVENTS": true, + "IN_ATTRIB": true, + "IN_CLASSA_HOST": true, + "IN_CLASSA_MAX": true, + "IN_CLASSA_NET": true, + "IN_CLASSA_NSHIFT": true, + "IN_CLASSB_HOST": true, + "IN_CLASSB_MAX": true, + "IN_CLASSB_NET": true, + "IN_CLASSB_NSHIFT": true, + "IN_CLASSC_HOST": true, + "IN_CLASSC_NET": true, + "IN_CLASSC_NSHIFT": true, + "IN_CLASSD_HOST": true, + "IN_CLASSD_NET": true, + "IN_CLASSD_NSHIFT": true, + "IN_CLOEXEC": true, + "IN_CLOSE": true, + "IN_CLOSE_NOWRITE": true, + "IN_CLOSE_WRITE": true, + "IN_CREATE": true, + "IN_DELETE": true, + "IN_DELETE_SELF": true, + "IN_DONT_FOLLOW": true, + "IN_EXCL_UNLINK": true, + "IN_IGNORED": true, + "IN_ISDIR": true, + "IN_LINKLOCALNETNUM": true, + "IN_LOOPBACKNET": true, + "IN_MASK_ADD": true, + "IN_MODIFY": true, + "IN_MOVE": true, + "IN_MOVED_FROM": true, + "IN_MOVED_TO": true, + "IN_MOVE_SELF": true, + "IN_NONBLOCK": true, + "IN_ONESHOT": true, + "IN_ONLYDIR": true, + "IN_OPEN": true, + "IN_Q_OVERFLOW": true, + "IN_RFC3021_HOST": true, + "IN_RFC3021_MASK": true, + "IN_RFC3021_NET": true, + "IN_RFC3021_NSHIFT": true, + "IN_UNMOUNT": true, + "IOC_IN": true, + "IOC_INOUT": true, + "IOC_OUT": true, + "IOC_VENDOR": true, + "IOC_WS2": true, + "IO_REPARSE_TAG_SYMLINK": true, + "IPMreq": true, + "IPMreqn": true, + "IPPROTO_3PC": true, + "IPPROTO_ADFS": true, + "IPPROTO_AH": true, + "IPPROTO_AHIP": true, + "IPPROTO_APES": true, + "IPPROTO_ARGUS": true, + "IPPROTO_AX25": true, + "IPPROTO_BHA": true, + "IPPROTO_BLT": true, + "IPPROTO_BRSATMON": true, + "IPPROTO_CARP": true, + "IPPROTO_CFTP": true, + "IPPROTO_CHAOS": true, + "IPPROTO_CMTP": true, + "IPPROTO_COMP": true, + "IPPROTO_CPHB": true, + "IPPROTO_CPNX": true, + "IPPROTO_DCCP": true, + "IPPROTO_DDP": true, + "IPPROTO_DGP": true, + "IPPROTO_DIVERT": true, + "IPPROTO_DIVERT_INIT": true, + "IPPROTO_DIVERT_RESP": true, + "IPPROTO_DONE": true, + "IPPROTO_DSTOPTS": true, + "IPPROTO_EGP": true, + "IPPROTO_EMCON": true, + "IPPROTO_ENCAP": true, + "IPPROTO_EON": true, + "IPPROTO_ESP": true, + "IPPROTO_ETHERIP": true, + "IPPROTO_FRAGMENT": true, + "IPPROTO_GGP": true, + "IPPROTO_GMTP": true, + "IPPROTO_GRE": true, + "IPPROTO_HELLO": true, + "IPPROTO_HMP": true, + "IPPROTO_HOPOPTS": true, + "IPPROTO_ICMP": true, + "IPPROTO_ICMPV6": true, + "IPPROTO_IDP": true, + "IPPROTO_IDPR": true, + "IPPROTO_IDRP": true, + "IPPROTO_IGMP": true, + "IPPROTO_IGP": true, + "IPPROTO_IGRP": true, + "IPPROTO_IL": true, + "IPPROTO_INLSP": true, + "IPPROTO_INP": true, + "IPPROTO_IP": true, + "IPPROTO_IPCOMP": true, + "IPPROTO_IPCV": true, + "IPPROTO_IPEIP": true, + "IPPROTO_IPIP": true, + "IPPROTO_IPPC": true, + "IPPROTO_IPV4": true, + "IPPROTO_IPV6": true, + "IPPROTO_IPV6_ICMP": true, + "IPPROTO_IRTP": true, + "IPPROTO_KRYPTOLAN": true, + "IPPROTO_LARP": true, + "IPPROTO_LEAF1": true, + "IPPROTO_LEAF2": true, + "IPPROTO_MAX": true, + "IPPROTO_MAXID": true, + "IPPROTO_MEAS": true, + "IPPROTO_MH": true, + "IPPROTO_MHRP": true, + "IPPROTO_MICP": true, + "IPPROTO_MOBILE": true, + "IPPROTO_MPLS": true, + "IPPROTO_MTP": true, + "IPPROTO_MUX": true, + "IPPROTO_ND": true, + "IPPROTO_NHRP": true, + "IPPROTO_NONE": true, + "IPPROTO_NSP": true, + "IPPROTO_NVPII": true, + "IPPROTO_OLD_DIVERT": true, + "IPPROTO_OSPFIGP": true, + "IPPROTO_PFSYNC": true, + "IPPROTO_PGM": true, + "IPPROTO_PIGP": true, + "IPPROTO_PIM": true, + "IPPROTO_PRM": true, + "IPPROTO_PUP": true, + "IPPROTO_PVP": true, + "IPPROTO_RAW": true, + "IPPROTO_RCCMON": true, + "IPPROTO_RDP": true, + "IPPROTO_ROUTING": true, + "IPPROTO_RSVP": true, + "IPPROTO_RVD": true, + "IPPROTO_SATEXPAK": true, + "IPPROTO_SATMON": true, + "IPPROTO_SCCSP": true, + "IPPROTO_SCTP": true, + "IPPROTO_SDRP": true, + "IPPROTO_SEND": true, + "IPPROTO_SEP": true, + "IPPROTO_SKIP": true, + "IPPROTO_SPACER": true, + "IPPROTO_SRPC": true, + "IPPROTO_ST": true, + "IPPROTO_SVMTP": true, + "IPPROTO_SWIPE": true, + "IPPROTO_TCF": true, + "IPPROTO_TCP": true, + "IPPROTO_TLSP": true, + "IPPROTO_TP": true, + "IPPROTO_TPXX": true, + "IPPROTO_TRUNK1": true, + "IPPROTO_TRUNK2": true, + "IPPROTO_TTP": true, + "IPPROTO_UDP": true, + "IPPROTO_UDPLITE": true, + "IPPROTO_VINES": true, + "IPPROTO_VISA": true, + "IPPROTO_VMTP": true, + "IPPROTO_VRRP": true, + "IPPROTO_WBEXPAK": true, + "IPPROTO_WBMON": true, + "IPPROTO_WSN": true, + "IPPROTO_XNET": true, + "IPPROTO_XTP": true, + "IPV6_2292DSTOPTS": true, + "IPV6_2292HOPLIMIT": true, + "IPV6_2292HOPOPTS": true, + "IPV6_2292NEXTHOP": true, + "IPV6_2292PKTINFO": true, + "IPV6_2292PKTOPTIONS": true, + "IPV6_2292RTHDR": true, + "IPV6_ADDRFORM": true, + "IPV6_ADD_MEMBERSHIP": true, + "IPV6_AUTHHDR": true, + "IPV6_AUTH_LEVEL": true, + "IPV6_AUTOFLOWLABEL": true, + "IPV6_BINDANY": true, + "IPV6_BINDV6ONLY": true, + "IPV6_BOUND_IF": true, + "IPV6_CHECKSUM": true, + "IPV6_DEFAULT_MULTICAST_HOPS": true, + "IPV6_DEFAULT_MULTICAST_LOOP": true, + "IPV6_DEFHLIM": true, + "IPV6_DONTFRAG": true, + "IPV6_DROP_MEMBERSHIP": true, + "IPV6_DSTOPTS": true, + "IPV6_ESP_NETWORK_LEVEL": true, + "IPV6_ESP_TRANS_LEVEL": true, + "IPV6_FAITH": true, + "IPV6_FLOWINFO_MASK": true, + "IPV6_FLOWLABEL_MASK": true, + "IPV6_FRAGTTL": true, + "IPV6_FW_ADD": true, + "IPV6_FW_DEL": true, + "IPV6_FW_FLUSH": true, + "IPV6_FW_GET": true, + "IPV6_FW_ZERO": true, + "IPV6_HLIMDEC": true, + "IPV6_HOPLIMIT": true, + "IPV6_HOPOPTS": true, + "IPV6_IPCOMP_LEVEL": true, + "IPV6_IPSEC_POLICY": true, + "IPV6_JOIN_ANYCAST": true, + "IPV6_JOIN_GROUP": true, + "IPV6_LEAVE_ANYCAST": true, + "IPV6_LEAVE_GROUP": true, + "IPV6_MAXHLIM": true, + "IPV6_MAXOPTHDR": true, + "IPV6_MAXPACKET": true, + "IPV6_MAX_GROUP_SRC_FILTER": true, + "IPV6_MAX_MEMBERSHIPS": true, + "IPV6_MAX_SOCK_SRC_FILTER": true, + "IPV6_MIN_MEMBERSHIPS": true, + "IPV6_MMTU": true, + "IPV6_MSFILTER": true, + "IPV6_MTU": true, + "IPV6_MTU_DISCOVER": true, + "IPV6_MULTICAST_HOPS": true, + "IPV6_MULTICAST_IF": true, + "IPV6_MULTICAST_LOOP": true, + "IPV6_NEXTHOP": true, + "IPV6_OPTIONS": true, + "IPV6_PATHMTU": true, + "IPV6_PIPEX": true, + "IPV6_PKTINFO": true, + "IPV6_PMTUDISC_DO": true, + "IPV6_PMTUDISC_DONT": true, + "IPV6_PMTUDISC_PROBE": true, + "IPV6_PMTUDISC_WANT": true, + "IPV6_PORTRANGE": true, + "IPV6_PORTRANGE_DEFAULT": true, + "IPV6_PORTRANGE_HIGH": true, + "IPV6_PORTRANGE_LOW": true, + "IPV6_PREFER_TEMPADDR": true, + "IPV6_RECVDSTOPTS": true, + "IPV6_RECVDSTPORT": true, + "IPV6_RECVERR": true, + "IPV6_RECVHOPLIMIT": true, + "IPV6_RECVHOPOPTS": true, + "IPV6_RECVPATHMTU": true, + "IPV6_RECVPKTINFO": true, + "IPV6_RECVRTHDR": true, + "IPV6_RECVTCLASS": true, + "IPV6_ROUTER_ALERT": true, + "IPV6_RTABLE": true, + "IPV6_RTHDR": true, + "IPV6_RTHDRDSTOPTS": true, + "IPV6_RTHDR_LOOSE": true, + "IPV6_RTHDR_STRICT": true, + "IPV6_RTHDR_TYPE_0": true, + "IPV6_RXDSTOPTS": true, + "IPV6_RXHOPOPTS": true, + "IPV6_SOCKOPT_RESERVED1": true, + "IPV6_TCLASS": true, + "IPV6_UNICAST_HOPS": true, + "IPV6_USE_MIN_MTU": true, + "IPV6_V6ONLY": true, + "IPV6_VERSION": true, + "IPV6_VERSION_MASK": true, + "IPV6_XFRM_POLICY": true, + "IP_ADD_MEMBERSHIP": true, + "IP_ADD_SOURCE_MEMBERSHIP": true, + "IP_AUTH_LEVEL": true, + "IP_BINDANY": true, + "IP_BLOCK_SOURCE": true, + "IP_BOUND_IF": true, + "IP_DEFAULT_MULTICAST_LOOP": true, + "IP_DEFAULT_MULTICAST_TTL": true, + "IP_DF": true, + "IP_DIVERTFL": true, + "IP_DONTFRAG": true, + "IP_DROP_MEMBERSHIP": true, + "IP_DROP_SOURCE_MEMBERSHIP": true, + "IP_DUMMYNET3": true, + "IP_DUMMYNET_CONFIGURE": true, + "IP_DUMMYNET_DEL": true, + "IP_DUMMYNET_FLUSH": true, + "IP_DUMMYNET_GET": true, + "IP_EF": true, + "IP_ERRORMTU": true, + "IP_ESP_NETWORK_LEVEL": true, + "IP_ESP_TRANS_LEVEL": true, + "IP_FAITH": true, + "IP_FREEBIND": true, + "IP_FW3": true, + "IP_FW_ADD": true, + "IP_FW_DEL": true, + "IP_FW_FLUSH": true, + "IP_FW_GET": true, + "IP_FW_NAT_CFG": true, + "IP_FW_NAT_DEL": true, + "IP_FW_NAT_GET_CONFIG": true, + "IP_FW_NAT_GET_LOG": true, + "IP_FW_RESETLOG": true, + "IP_FW_TABLE_ADD": true, + "IP_FW_TABLE_DEL": true, + "IP_FW_TABLE_FLUSH": true, + "IP_FW_TABLE_GETSIZE": true, + "IP_FW_TABLE_LIST": true, + "IP_FW_ZERO": true, + "IP_HDRINCL": true, + "IP_IPCOMP_LEVEL": true, + "IP_IPSECFLOWINFO": true, + "IP_IPSEC_LOCAL_AUTH": true, + "IP_IPSEC_LOCAL_CRED": true, + "IP_IPSEC_LOCAL_ID": true, + "IP_IPSEC_POLICY": true, + "IP_IPSEC_REMOTE_AUTH": true, + "IP_IPSEC_REMOTE_CRED": true, + "IP_IPSEC_REMOTE_ID": true, + "IP_MAXPACKET": true, + "IP_MAX_GROUP_SRC_FILTER": true, + "IP_MAX_MEMBERSHIPS": true, + "IP_MAX_SOCK_MUTE_FILTER": true, + "IP_MAX_SOCK_SRC_FILTER": true, + "IP_MAX_SOURCE_FILTER": true, + "IP_MF": true, + "IP_MINFRAGSIZE": true, + "IP_MINTTL": true, + "IP_MIN_MEMBERSHIPS": true, + "IP_MSFILTER": true, + "IP_MSS": true, + "IP_MTU": true, + "IP_MTU_DISCOVER": true, + "IP_MULTICAST_IF": true, + "IP_MULTICAST_IFINDEX": true, + "IP_MULTICAST_LOOP": true, + "IP_MULTICAST_TTL": true, + "IP_MULTICAST_VIF": true, + "IP_NAT__XXX": true, + "IP_OFFMASK": true, + "IP_OLD_FW_ADD": true, + "IP_OLD_FW_DEL": true, + "IP_OLD_FW_FLUSH": true, + "IP_OLD_FW_GET": true, + "IP_OLD_FW_RESETLOG": true, + "IP_OLD_FW_ZERO": true, + "IP_ONESBCAST": true, + "IP_OPTIONS": true, + "IP_ORIGDSTADDR": true, + "IP_PASSSEC": true, + "IP_PIPEX": true, + "IP_PKTINFO": true, + "IP_PKTOPTIONS": true, + "IP_PMTUDISC": true, + "IP_PMTUDISC_DO": true, + "IP_PMTUDISC_DONT": true, + "IP_PMTUDISC_PROBE": true, + "IP_PMTUDISC_WANT": true, + "IP_PORTRANGE": true, + "IP_PORTRANGE_DEFAULT": true, + "IP_PORTRANGE_HIGH": true, + "IP_PORTRANGE_LOW": true, + "IP_RECVDSTADDR": true, + "IP_RECVDSTPORT": true, + "IP_RECVERR": true, + "IP_RECVIF": true, + "IP_RECVOPTS": true, + "IP_RECVORIGDSTADDR": true, + "IP_RECVPKTINFO": true, + "IP_RECVRETOPTS": true, + "IP_RECVRTABLE": true, + "IP_RECVTOS": true, + "IP_RECVTTL": true, + "IP_RETOPTS": true, + "IP_RF": true, + "IP_ROUTER_ALERT": true, + "IP_RSVP_OFF": true, + "IP_RSVP_ON": true, + "IP_RSVP_VIF_OFF": true, + "IP_RSVP_VIF_ON": true, + "IP_RTABLE": true, + "IP_SENDSRCADDR": true, + "IP_STRIPHDR": true, + "IP_TOS": true, + "IP_TRAFFIC_MGT_BACKGROUND": true, + "IP_TRANSPARENT": true, + "IP_TTL": true, + "IP_UNBLOCK_SOURCE": true, + "IP_XFRM_POLICY": true, + "IPv6MTUInfo": true, + "IPv6Mreq": true, + "ISIG": true, + "ISTRIP": true, + "IUCLC": true, + "IUTF8": true, + "IXANY": true, + "IXOFF": true, + "IXON": true, + "IfAddrmsg": true, + "IfAnnounceMsghdr": true, + "IfData": true, + "IfInfomsg": true, + "IfMsghdr": true, + "IfaMsghdr": true, + "IfmaMsghdr": true, + "IfmaMsghdr2": true, + "ImplementsGetwd": true, + "Inet4Pktinfo": true, + "Inet6Pktinfo": true, + "InotifyAddWatch": true, + "InotifyEvent": true, + "InotifyInit": true, + "InotifyInit1": true, + "InotifyRmWatch": true, + "InterfaceAddrMessage": true, + "InterfaceAnnounceMessage": true, + "InterfaceInfo": true, + "InterfaceMessage": true, + "InterfaceMulticastAddrMessage": true, + "InvalidHandle": true, + "Ioperm": true, + "Iopl": true, + "Iovec": true, + "IpAdapterInfo": true, + "IpAddrString": true, + "IpAddressString": true, + "IpMaskString": true, + "Issetugid": true, + "KEY_ALL_ACCESS": true, + "KEY_CREATE_LINK": true, + "KEY_CREATE_SUB_KEY": true, + "KEY_ENUMERATE_SUB_KEYS": true, + "KEY_EXECUTE": true, + "KEY_NOTIFY": true, + "KEY_QUERY_VALUE": true, + "KEY_READ": true, + "KEY_SET_VALUE": true, + "KEY_WOW64_32KEY": true, + "KEY_WOW64_64KEY": true, + "KEY_WRITE": true, + "Kevent": true, + "Kevent_t": true, + "Kill": true, + "Klogctl": true, + "Kqueue": true, + "LANG_ENGLISH": true, + "LAYERED_PROTOCOL": true, + "LCNT_OVERLOAD_FLUSH": true, + "LINUX_REBOOT_CMD_CAD_OFF": true, + "LINUX_REBOOT_CMD_CAD_ON": true, + "LINUX_REBOOT_CMD_HALT": true, + "LINUX_REBOOT_CMD_KEXEC": true, + "LINUX_REBOOT_CMD_POWER_OFF": true, + "LINUX_REBOOT_CMD_RESTART": true, + "LINUX_REBOOT_CMD_RESTART2": true, + "LINUX_REBOOT_CMD_SW_SUSPEND": true, + "LINUX_REBOOT_MAGIC1": true, + "LINUX_REBOOT_MAGIC2": true, + "LOCK_EX": true, + "LOCK_NB": true, + "LOCK_SH": true, + "LOCK_UN": true, + "LazyDLL": true, + "LazyProc": true, + "Lchown": true, + "Linger": true, + "Link": true, + "Listen": true, + "Listxattr": true, + "LoadCancelIoEx": true, + "LoadConnectEx": true, + "LoadCreateSymbolicLink": true, + "LoadDLL": true, + "LoadGetAddrInfo": true, + "LoadLibrary": true, + "LoadSetFileCompletionNotificationModes": true, + "LocalFree": true, + "Log2phys_t": true, + "LookupAccountName": true, + "LookupAccountSid": true, + "LookupSID": true, + "LsfJump": true, + "LsfSocket": true, + "LsfStmt": true, + "Lstat": true, + "MADV_AUTOSYNC": true, + "MADV_CAN_REUSE": true, + "MADV_CORE": true, + "MADV_DOFORK": true, + "MADV_DONTFORK": true, + "MADV_DONTNEED": true, + "MADV_FREE": true, + "MADV_FREE_REUSABLE": true, + "MADV_FREE_REUSE": true, + "MADV_HUGEPAGE": true, + "MADV_HWPOISON": true, + "MADV_MERGEABLE": true, + "MADV_NOCORE": true, + "MADV_NOHUGEPAGE": true, + "MADV_NORMAL": true, + "MADV_NOSYNC": true, + "MADV_PROTECT": true, + "MADV_RANDOM": true, + "MADV_REMOVE": true, + "MADV_SEQUENTIAL": true, + "MADV_SPACEAVAIL": true, + "MADV_UNMERGEABLE": true, + "MADV_WILLNEED": true, + "MADV_ZERO_WIRED_PAGES": true, + "MAP_32BIT": true, + "MAP_ALIGNED_SUPER": true, + "MAP_ALIGNMENT_16MB": true, + "MAP_ALIGNMENT_1TB": true, + "MAP_ALIGNMENT_256TB": true, + "MAP_ALIGNMENT_4GB": true, + "MAP_ALIGNMENT_64KB": true, + "MAP_ALIGNMENT_64PB": true, + "MAP_ALIGNMENT_MASK": true, + "MAP_ALIGNMENT_SHIFT": true, + "MAP_ANON": true, + "MAP_ANONYMOUS": true, + "MAP_COPY": true, + "MAP_DENYWRITE": true, + "MAP_EXECUTABLE": true, + "MAP_FILE": true, + "MAP_FIXED": true, + "MAP_FLAGMASK": true, + "MAP_GROWSDOWN": true, + "MAP_HASSEMAPHORE": true, + "MAP_HUGETLB": true, + "MAP_INHERIT": true, + "MAP_INHERIT_COPY": true, + "MAP_INHERIT_DEFAULT": true, + "MAP_INHERIT_DONATE_COPY": true, + "MAP_INHERIT_NONE": true, + "MAP_INHERIT_SHARE": true, + "MAP_JIT": true, + "MAP_LOCKED": true, + "MAP_NOCACHE": true, + "MAP_NOCORE": true, + "MAP_NOEXTEND": true, + "MAP_NONBLOCK": true, + "MAP_NORESERVE": true, + "MAP_NOSYNC": true, + "MAP_POPULATE": true, + "MAP_PREFAULT_READ": true, + "MAP_PRIVATE": true, + "MAP_RENAME": true, + "MAP_RESERVED0080": true, + "MAP_RESERVED0100": true, + "MAP_SHARED": true, + "MAP_STACK": true, + "MAP_TRYFIXED": true, + "MAP_TYPE": true, + "MAP_WIRED": true, + "MAXIMUM_REPARSE_DATA_BUFFER_SIZE": true, + "MAXLEN_IFDESCR": true, + "MAXLEN_PHYSADDR": true, + "MAX_ADAPTER_ADDRESS_LENGTH": true, + "MAX_ADAPTER_DESCRIPTION_LENGTH": true, + "MAX_ADAPTER_NAME_LENGTH": true, + "MAX_COMPUTERNAME_LENGTH": true, + "MAX_INTERFACE_NAME_LEN": true, + "MAX_LONG_PATH": true, + "MAX_PATH": true, + "MAX_PROTOCOL_CHAIN": true, + "MCL_CURRENT": true, + "MCL_FUTURE": true, + "MNT_DETACH": true, + "MNT_EXPIRE": true, + "MNT_FORCE": true, + "MSG_BCAST": true, + "MSG_CMSG_CLOEXEC": true, + "MSG_COMPAT": true, + "MSG_CONFIRM": true, + "MSG_CONTROLMBUF": true, + "MSG_CTRUNC": true, + "MSG_DONTROUTE": true, + "MSG_DONTWAIT": true, + "MSG_EOF": true, + "MSG_EOR": true, + "MSG_ERRQUEUE": true, + "MSG_FASTOPEN": true, + "MSG_FIN": true, + "MSG_FLUSH": true, + "MSG_HAVEMORE": true, + "MSG_HOLD": true, + "MSG_IOVUSRSPACE": true, + "MSG_LENUSRSPACE": true, + "MSG_MCAST": true, + "MSG_MORE": true, + "MSG_NAMEMBUF": true, + "MSG_NBIO": true, + "MSG_NEEDSA": true, + "MSG_NOSIGNAL": true, + "MSG_NOTIFICATION": true, + "MSG_OOB": true, + "MSG_PEEK": true, + "MSG_PROXY": true, + "MSG_RCVMORE": true, + "MSG_RST": true, + "MSG_SEND": true, + "MSG_SYN": true, + "MSG_TRUNC": true, + "MSG_TRYHARD": true, + "MSG_USERFLAGS": true, + "MSG_WAITALL": true, + "MSG_WAITFORONE": true, + "MSG_WAITSTREAM": true, + "MS_ACTIVE": true, + "MS_ASYNC": true, + "MS_BIND": true, + "MS_DEACTIVATE": true, + "MS_DIRSYNC": true, + "MS_INVALIDATE": true, + "MS_I_VERSION": true, + "MS_KERNMOUNT": true, + "MS_KILLPAGES": true, + "MS_MANDLOCK": true, + "MS_MGC_MSK": true, + "MS_MGC_VAL": true, + "MS_MOVE": true, + "MS_NOATIME": true, + "MS_NODEV": true, + "MS_NODIRATIME": true, + "MS_NOEXEC": true, + "MS_NOSUID": true, + "MS_NOUSER": true, + "MS_POSIXACL": true, + "MS_PRIVATE": true, + "MS_RDONLY": true, + "MS_REC": true, + "MS_RELATIME": true, + "MS_REMOUNT": true, + "MS_RMT_MASK": true, + "MS_SHARED": true, + "MS_SILENT": true, + "MS_SLAVE": true, + "MS_STRICTATIME": true, + "MS_SYNC": true, + "MS_SYNCHRONOUS": true, + "MS_UNBINDABLE": true, + "Madvise": true, + "MapViewOfFile": true, + "MaxTokenInfoClass": true, + "Mclpool": true, + "MibIfRow": true, + "Mkdir": true, + "Mkdirat": true, + "Mkfifo": true, + "Mknod": true, + "Mknodat": true, + "Mlock": true, + "Mlockall": true, + "Mmap": true, + "Mount": true, + "MoveFile": true, + "Mprotect": true, + "Msghdr": true, + "Munlock": true, + "Munlockall": true, + "Munmap": true, + "MustLoadDLL": true, + "NAME_MAX": true, + "NETLINK_ADD_MEMBERSHIP": true, + "NETLINK_AUDIT": true, + "NETLINK_BROADCAST_ERROR": true, + "NETLINK_CONNECTOR": true, + "NETLINK_DNRTMSG": true, + "NETLINK_DROP_MEMBERSHIP": true, + "NETLINK_ECRYPTFS": true, + "NETLINK_FIB_LOOKUP": true, + "NETLINK_FIREWALL": true, + "NETLINK_GENERIC": true, + "NETLINK_INET_DIAG": true, + "NETLINK_IP6_FW": true, + "NETLINK_ISCSI": true, + "NETLINK_KOBJECT_UEVENT": true, + "NETLINK_NETFILTER": true, + "NETLINK_NFLOG": true, + "NETLINK_NO_ENOBUFS": true, + "NETLINK_PKTINFO": true, + "NETLINK_RDMA": true, + "NETLINK_ROUTE": true, + "NETLINK_SCSITRANSPORT": true, + "NETLINK_SELINUX": true, + "NETLINK_UNUSED": true, + "NETLINK_USERSOCK": true, + "NETLINK_XFRM": true, + "NET_RT_DUMP": true, + "NET_RT_DUMP2": true, + "NET_RT_FLAGS": true, + "NET_RT_IFLIST": true, + "NET_RT_IFLIST2": true, + "NET_RT_IFLISTL": true, + "NET_RT_IFMALIST": true, + "NET_RT_MAXID": true, + "NET_RT_OIFLIST": true, + "NET_RT_OOIFLIST": true, + "NET_RT_STAT": true, + "NET_RT_STATS": true, + "NET_RT_TABLE": true, + "NET_RT_TRASH": true, + "NLA_ALIGNTO": true, + "NLA_F_NESTED": true, + "NLA_F_NET_BYTEORDER": true, + "NLA_HDRLEN": true, + "NLMSG_ALIGNTO": true, + "NLMSG_DONE": true, + "NLMSG_ERROR": true, + "NLMSG_HDRLEN": true, + "NLMSG_MIN_TYPE": true, + "NLMSG_NOOP": true, + "NLMSG_OVERRUN": true, + "NLM_F_ACK": true, + "NLM_F_APPEND": true, + "NLM_F_ATOMIC": true, + "NLM_F_CREATE": true, + "NLM_F_DUMP": true, + "NLM_F_ECHO": true, + "NLM_F_EXCL": true, + "NLM_F_MATCH": true, + "NLM_F_MULTI": true, + "NLM_F_REPLACE": true, + "NLM_F_REQUEST": true, + "NLM_F_ROOT": true, + "NOFLSH": true, + "NOTE_ABSOLUTE": true, + "NOTE_ATTRIB": true, + "NOTE_CHILD": true, + "NOTE_DELETE": true, + "NOTE_EOF": true, + "NOTE_EXEC": true, + "NOTE_EXIT": true, + "NOTE_EXITSTATUS": true, + "NOTE_EXTEND": true, + "NOTE_FFAND": true, + "NOTE_FFCOPY": true, + "NOTE_FFCTRLMASK": true, + "NOTE_FFLAGSMASK": true, + "NOTE_FFNOP": true, + "NOTE_FFOR": true, + "NOTE_FORK": true, + "NOTE_LINK": true, + "NOTE_LOWAT": true, + "NOTE_NONE": true, + "NOTE_NSECONDS": true, + "NOTE_PCTRLMASK": true, + "NOTE_PDATAMASK": true, + "NOTE_REAP": true, + "NOTE_RENAME": true, + "NOTE_RESOURCEEND": true, + "NOTE_REVOKE": true, + "NOTE_SECONDS": true, + "NOTE_SIGNAL": true, + "NOTE_TRACK": true, + "NOTE_TRACKERR": true, + "NOTE_TRIGGER": true, + "NOTE_TRUNCATE": true, + "NOTE_USECONDS": true, + "NOTE_VM_ERROR": true, + "NOTE_VM_PRESSURE": true, + "NOTE_VM_PRESSURE_SUDDEN_TERMINATE": true, + "NOTE_VM_PRESSURE_TERMINATE": true, + "NOTE_WRITE": true, + "NameCanonical": true, + "NameCanonicalEx": true, + "NameDisplay": true, + "NameDnsDomain": true, + "NameFullyQualifiedDN": true, + "NameSamCompatible": true, + "NameServicePrincipal": true, + "NameUniqueId": true, + "NameUnknown": true, + "NameUserPrincipal": true, + "Nanosleep": true, + "NetApiBufferFree": true, + "NetGetJoinInformation": true, + "NetSetupDomainName": true, + "NetSetupUnjoined": true, + "NetSetupUnknownStatus": true, + "NetSetupWorkgroupName": true, + "NetUserGetInfo": true, + "NetlinkMessage": true, + "NetlinkRIB": true, + "NetlinkRouteAttr": true, + "NetlinkRouteRequest": true, + "NewCallback": true, + "NewCallbackCDecl": true, + "NewLazyDLL": true, + "NlAttr": true, + "NlMsgerr": true, + "NlMsghdr": true, + "NsecToFiletime": true, + "NsecToTimespec": true, + "NsecToTimeval": true, + "Ntohs": true, + "OCRNL": true, + "OFDEL": true, + "OFILL": true, + "OFIOGETBMAP": true, + "OID_PKIX_KP_SERVER_AUTH": true, + "OID_SERVER_GATED_CRYPTO": true, + "OID_SGC_NETSCAPE": true, + "OLCUC": true, + "ONLCR": true, + "ONLRET": true, + "ONOCR": true, + "ONOEOT": true, + "OPEN_ALWAYS": true, + "OPEN_EXISTING": true, + "OPOST": true, + "O_ACCMODE": true, + "O_ALERT": true, + "O_ALT_IO": true, + "O_APPEND": true, + "O_ASYNC": true, + "O_CLOEXEC": true, + "O_CREAT": true, + "O_DIRECT": true, + "O_DIRECTORY": true, + "O_DSYNC": true, + "O_EVTONLY": true, + "O_EXCL": true, + "O_EXEC": true, + "O_EXLOCK": true, + "O_FSYNC": true, + "O_LARGEFILE": true, + "O_NDELAY": true, + "O_NOATIME": true, + "O_NOCTTY": true, + "O_NOFOLLOW": true, + "O_NONBLOCK": true, + "O_NOSIGPIPE": true, + "O_POPUP": true, + "O_RDONLY": true, + "O_RDWR": true, + "O_RSYNC": true, + "O_SHLOCK": true, + "O_SYMLINK": true, + "O_SYNC": true, + "O_TRUNC": true, + "O_TTY_INIT": true, + "O_WRONLY": true, + "Open": true, + "OpenCurrentProcessToken": true, + "OpenProcess": true, + "OpenProcessToken": true, + "Openat": true, + "Overlapped": true, + "PACKET_ADD_MEMBERSHIP": true, + "PACKET_BROADCAST": true, + "PACKET_DROP_MEMBERSHIP": true, + "PACKET_FASTROUTE": true, + "PACKET_HOST": true, + "PACKET_LOOPBACK": true, + "PACKET_MR_ALLMULTI": true, + "PACKET_MR_MULTICAST": true, + "PACKET_MR_PROMISC": true, + "PACKET_MULTICAST": true, + "PACKET_OTHERHOST": true, + "PACKET_OUTGOING": true, + "PACKET_RECV_OUTPUT": true, + "PACKET_RX_RING": true, + "PACKET_STATISTICS": true, + "PAGE_EXECUTE_READ": true, + "PAGE_EXECUTE_READWRITE": true, + "PAGE_EXECUTE_WRITECOPY": true, + "PAGE_READONLY": true, + "PAGE_READWRITE": true, + "PAGE_WRITECOPY": true, + "PARENB": true, + "PARMRK": true, + "PARODD": true, + "PENDIN": true, + "PFL_HIDDEN": true, + "PFL_MATCHES_PROTOCOL_ZERO": true, + "PFL_MULTIPLE_PROTO_ENTRIES": true, + "PFL_NETWORKDIRECT_PROVIDER": true, + "PFL_RECOMMENDED_PROTO_ENTRY": true, + "PF_FLUSH": true, + "PKCS_7_ASN_ENCODING": true, + "PMC5_PIPELINE_FLUSH": true, + "PRIO_PGRP": true, + "PRIO_PROCESS": true, + "PRIO_USER": true, + "PRI_IOFLUSH": true, + "PROCESS_QUERY_INFORMATION": true, + "PROCESS_TERMINATE": true, + "PROT_EXEC": true, + "PROT_GROWSDOWN": true, + "PROT_GROWSUP": true, + "PROT_NONE": true, + "PROT_READ": true, + "PROT_WRITE": true, + "PROV_DH_SCHANNEL": true, + "PROV_DSS": true, + "PROV_DSS_DH": true, + "PROV_EC_ECDSA_FULL": true, + "PROV_EC_ECDSA_SIG": true, + "PROV_EC_ECNRA_FULL": true, + "PROV_EC_ECNRA_SIG": true, + "PROV_FORTEZZA": true, + "PROV_INTEL_SEC": true, + "PROV_MS_EXCHANGE": true, + "PROV_REPLACE_OWF": true, + "PROV_RNG": true, + "PROV_RSA_AES": true, + "PROV_RSA_FULL": true, + "PROV_RSA_SCHANNEL": true, + "PROV_RSA_SIG": true, + "PROV_SPYRUS_LYNKS": true, + "PROV_SSL": true, + "PR_CAPBSET_DROP": true, + "PR_CAPBSET_READ": true, + "PR_CLEAR_SECCOMP_FILTER": true, + "PR_ENDIAN_BIG": true, + "PR_ENDIAN_LITTLE": true, + "PR_ENDIAN_PPC_LITTLE": true, + "PR_FPEMU_NOPRINT": true, + "PR_FPEMU_SIGFPE": true, + "PR_FP_EXC_ASYNC": true, + "PR_FP_EXC_DISABLED": true, + "PR_FP_EXC_DIV": true, + "PR_FP_EXC_INV": true, + "PR_FP_EXC_NONRECOV": true, + "PR_FP_EXC_OVF": true, + "PR_FP_EXC_PRECISE": true, + "PR_FP_EXC_RES": true, + "PR_FP_EXC_SW_ENABLE": true, + "PR_FP_EXC_UND": true, + "PR_GET_DUMPABLE": true, + "PR_GET_ENDIAN": true, + "PR_GET_FPEMU": true, + "PR_GET_FPEXC": true, + "PR_GET_KEEPCAPS": true, + "PR_GET_NAME": true, + "PR_GET_PDEATHSIG": true, + "PR_GET_SECCOMP": true, + "PR_GET_SECCOMP_FILTER": true, + "PR_GET_SECUREBITS": true, + "PR_GET_TIMERSLACK": true, + "PR_GET_TIMING": true, + "PR_GET_TSC": true, + "PR_GET_UNALIGN": true, + "PR_MCE_KILL": true, + "PR_MCE_KILL_CLEAR": true, + "PR_MCE_KILL_DEFAULT": true, + "PR_MCE_KILL_EARLY": true, + "PR_MCE_KILL_GET": true, + "PR_MCE_KILL_LATE": true, + "PR_MCE_KILL_SET": true, + "PR_SECCOMP_FILTER_EVENT": true, + "PR_SECCOMP_FILTER_SYSCALL": true, + "PR_SET_DUMPABLE": true, + "PR_SET_ENDIAN": true, + "PR_SET_FPEMU": true, + "PR_SET_FPEXC": true, + "PR_SET_KEEPCAPS": true, + "PR_SET_NAME": true, + "PR_SET_PDEATHSIG": true, + "PR_SET_PTRACER": true, + "PR_SET_SECCOMP": true, + "PR_SET_SECCOMP_FILTER": true, + "PR_SET_SECUREBITS": true, + "PR_SET_TIMERSLACK": true, + "PR_SET_TIMING": true, + "PR_SET_TSC": true, + "PR_SET_UNALIGN": true, + "PR_TASK_PERF_EVENTS_DISABLE": true, + "PR_TASK_PERF_EVENTS_ENABLE": true, + "PR_TIMING_STATISTICAL": true, + "PR_TIMING_TIMESTAMP": true, + "PR_TSC_ENABLE": true, + "PR_TSC_SIGSEGV": true, + "PR_UNALIGN_NOPRINT": true, + "PR_UNALIGN_SIGBUS": true, + "PTRACE_ARCH_PRCTL": true, + "PTRACE_ATTACH": true, + "PTRACE_CONT": true, + "PTRACE_DETACH": true, + "PTRACE_EVENT_CLONE": true, + "PTRACE_EVENT_EXEC": true, + "PTRACE_EVENT_EXIT": true, + "PTRACE_EVENT_FORK": true, + "PTRACE_EVENT_VFORK": true, + "PTRACE_EVENT_VFORK_DONE": true, + "PTRACE_GETCRUNCHREGS": true, + "PTRACE_GETEVENTMSG": true, + "PTRACE_GETFPREGS": true, + "PTRACE_GETFPXREGS": true, + "PTRACE_GETHBPREGS": true, + "PTRACE_GETREGS": true, + "PTRACE_GETREGSET": true, + "PTRACE_GETSIGINFO": true, + "PTRACE_GETVFPREGS": true, + "PTRACE_GETWMMXREGS": true, + "PTRACE_GET_THREAD_AREA": true, + "PTRACE_KILL": true, + "PTRACE_OLDSETOPTIONS": true, + "PTRACE_O_MASK": true, + "PTRACE_O_TRACECLONE": true, + "PTRACE_O_TRACEEXEC": true, + "PTRACE_O_TRACEEXIT": true, + "PTRACE_O_TRACEFORK": true, + "PTRACE_O_TRACESYSGOOD": true, + "PTRACE_O_TRACEVFORK": true, + "PTRACE_O_TRACEVFORKDONE": true, + "PTRACE_PEEKDATA": true, + "PTRACE_PEEKTEXT": true, + "PTRACE_PEEKUSR": true, + "PTRACE_POKEDATA": true, + "PTRACE_POKETEXT": true, + "PTRACE_POKEUSR": true, + "PTRACE_SETCRUNCHREGS": true, + "PTRACE_SETFPREGS": true, + "PTRACE_SETFPXREGS": true, + "PTRACE_SETHBPREGS": true, + "PTRACE_SETOPTIONS": true, + "PTRACE_SETREGS": true, + "PTRACE_SETREGSET": true, + "PTRACE_SETSIGINFO": true, + "PTRACE_SETVFPREGS": true, + "PTRACE_SETWMMXREGS": true, + "PTRACE_SET_SYSCALL": true, + "PTRACE_SET_THREAD_AREA": true, + "PTRACE_SINGLEBLOCK": true, + "PTRACE_SINGLESTEP": true, + "PTRACE_SYSCALL": true, + "PTRACE_SYSEMU": true, + "PTRACE_SYSEMU_SINGLESTEP": true, + "PTRACE_TRACEME": true, + "PT_ATTACH": true, + "PT_ATTACHEXC": true, + "PT_CONTINUE": true, + "PT_DATA_ADDR": true, + "PT_DENY_ATTACH": true, + "PT_DETACH": true, + "PT_FIRSTMACH": true, + "PT_FORCEQUOTA": true, + "PT_KILL": true, + "PT_MASK": true, + "PT_READ_D": true, + "PT_READ_I": true, + "PT_READ_U": true, + "PT_SIGEXC": true, + "PT_STEP": true, + "PT_TEXT_ADDR": true, + "PT_TEXT_END_ADDR": true, + "PT_THUPDATE": true, + "PT_TRACE_ME": true, + "PT_WRITE_D": true, + "PT_WRITE_I": true, + "PT_WRITE_U": true, + "ParseDirent": true, + "ParseNetlinkMessage": true, + "ParseNetlinkRouteAttr": true, + "ParseRoutingMessage": true, + "ParseRoutingSockaddr": true, + "ParseSocketControlMessage": true, + "ParseUnixCredentials": true, + "ParseUnixRights": true, + "PathMax": true, + "Pathconf": true, + "Pause": true, + "Pipe": true, + "Pipe2": true, + "PivotRoot": true, + "Pointer": true, + "PostQueuedCompletionStatus": true, + "Pread": true, + "Proc": true, + "ProcAttr": true, + "Process32First": true, + "Process32Next": true, + "ProcessEntry32": true, + "ProcessInformation": true, + "Protoent": true, + "PtraceAttach": true, + "PtraceCont": true, + "PtraceDetach": true, + "PtraceGetEventMsg": true, + "PtraceGetRegs": true, + "PtracePeekData": true, + "PtracePeekText": true, + "PtracePokeData": true, + "PtracePokeText": true, + "PtraceRegs": true, + "PtraceSetOptions": true, + "PtraceSetRegs": true, + "PtraceSingleStep": true, + "PtraceSyscall": true, + "Pwrite": true, + "REG_BINARY": true, + "REG_DWORD": true, + "REG_DWORD_BIG_ENDIAN": true, + "REG_DWORD_LITTLE_ENDIAN": true, + "REG_EXPAND_SZ": true, + "REG_FULL_RESOURCE_DESCRIPTOR": true, + "REG_LINK": true, + "REG_MULTI_SZ": true, + "REG_NONE": true, + "REG_QWORD": true, + "REG_QWORD_LITTLE_ENDIAN": true, + "REG_RESOURCE_LIST": true, + "REG_RESOURCE_REQUIREMENTS_LIST": true, + "REG_SZ": true, + "RLIMIT_AS": true, + "RLIMIT_CORE": true, + "RLIMIT_CPU": true, + "RLIMIT_DATA": true, + "RLIMIT_FSIZE": true, + "RLIMIT_NOFILE": true, + "RLIMIT_STACK": true, + "RLIM_INFINITY": true, + "RTAX_ADVMSS": true, + "RTAX_AUTHOR": true, + "RTAX_BRD": true, + "RTAX_CWND": true, + "RTAX_DST": true, + "RTAX_FEATURES": true, + "RTAX_FEATURE_ALLFRAG": true, + "RTAX_FEATURE_ECN": true, + "RTAX_FEATURE_SACK": true, + "RTAX_FEATURE_TIMESTAMP": true, + "RTAX_GATEWAY": true, + "RTAX_GENMASK": true, + "RTAX_HOPLIMIT": true, + "RTAX_IFA": true, + "RTAX_IFP": true, + "RTAX_INITCWND": true, + "RTAX_INITRWND": true, + "RTAX_LABEL": true, + "RTAX_LOCK": true, + "RTAX_MAX": true, + "RTAX_MTU": true, + "RTAX_NETMASK": true, + "RTAX_REORDERING": true, + "RTAX_RTO_MIN": true, + "RTAX_RTT": true, + "RTAX_RTTVAR": true, + "RTAX_SRC": true, + "RTAX_SRCMASK": true, + "RTAX_SSTHRESH": true, + "RTAX_TAG": true, + "RTAX_UNSPEC": true, + "RTAX_WINDOW": true, + "RTA_ALIGNTO": true, + "RTA_AUTHOR": true, + "RTA_BRD": true, + "RTA_CACHEINFO": true, + "RTA_DST": true, + "RTA_FLOW": true, + "RTA_GATEWAY": true, + "RTA_GENMASK": true, + "RTA_IFA": true, + "RTA_IFP": true, + "RTA_IIF": true, + "RTA_LABEL": true, + "RTA_MAX": true, + "RTA_METRICS": true, + "RTA_MULTIPATH": true, + "RTA_NETMASK": true, + "RTA_OIF": true, + "RTA_PREFSRC": true, + "RTA_PRIORITY": true, + "RTA_SRC": true, + "RTA_SRCMASK": true, + "RTA_TABLE": true, + "RTA_TAG": true, + "RTA_UNSPEC": true, + "RTCF_DIRECTSRC": true, + "RTCF_DOREDIRECT": true, + "RTCF_LOG": true, + "RTCF_MASQ": true, + "RTCF_NAT": true, + "RTCF_VALVE": true, + "RTF_ADDRCLASSMASK": true, + "RTF_ADDRCONF": true, + "RTF_ALLONLINK": true, + "RTF_ANNOUNCE": true, + "RTF_BLACKHOLE": true, + "RTF_BROADCAST": true, + "RTF_CACHE": true, + "RTF_CLONED": true, + "RTF_CLONING": true, + "RTF_CONDEMNED": true, + "RTF_DEFAULT": true, + "RTF_DELCLONE": true, + "RTF_DONE": true, + "RTF_DYNAMIC": true, + "RTF_FLOW": true, + "RTF_FMASK": true, + "RTF_GATEWAY": true, + "RTF_GWFLAG_COMPAT": true, + "RTF_HOST": true, + "RTF_IFREF": true, + "RTF_IFSCOPE": true, + "RTF_INTERFACE": true, + "RTF_IRTT": true, + "RTF_LINKRT": true, + "RTF_LLDATA": true, + "RTF_LLINFO": true, + "RTF_LOCAL": true, + "RTF_MASK": true, + "RTF_MODIFIED": true, + "RTF_MPATH": true, + "RTF_MPLS": true, + "RTF_MSS": true, + "RTF_MTU": true, + "RTF_MULTICAST": true, + "RTF_NAT": true, + "RTF_NOFORWARD": true, + "RTF_NONEXTHOP": true, + "RTF_NOPMTUDISC": true, + "RTF_PERMANENT_ARP": true, + "RTF_PINNED": true, + "RTF_POLICY": true, + "RTF_PRCLONING": true, + "RTF_PROTO1": true, + "RTF_PROTO2": true, + "RTF_PROTO3": true, + "RTF_REINSTATE": true, + "RTF_REJECT": true, + "RTF_RNH_LOCKED": true, + "RTF_SOURCE": true, + "RTF_SRC": true, + "RTF_STATIC": true, + "RTF_STICKY": true, + "RTF_THROW": true, + "RTF_TUNNEL": true, + "RTF_UP": true, + "RTF_USETRAILERS": true, + "RTF_WASCLONED": true, + "RTF_WINDOW": true, + "RTF_XRESOLVE": true, + "RTM_ADD": true, + "RTM_BASE": true, + "RTM_CHANGE": true, + "RTM_CHGADDR": true, + "RTM_DELACTION": true, + "RTM_DELADDR": true, + "RTM_DELADDRLABEL": true, + "RTM_DELETE": true, + "RTM_DELLINK": true, + "RTM_DELMADDR": true, + "RTM_DELNEIGH": true, + "RTM_DELQDISC": true, + "RTM_DELROUTE": true, + "RTM_DELRULE": true, + "RTM_DELTCLASS": true, + "RTM_DELTFILTER": true, + "RTM_DESYNC": true, + "RTM_F_CLONED": true, + "RTM_F_EQUALIZE": true, + "RTM_F_NOTIFY": true, + "RTM_F_PREFIX": true, + "RTM_GET": true, + "RTM_GET2": true, + "RTM_GETACTION": true, + "RTM_GETADDR": true, + "RTM_GETADDRLABEL": true, + "RTM_GETANYCAST": true, + "RTM_GETDCB": true, + "RTM_GETLINK": true, + "RTM_GETMULTICAST": true, + "RTM_GETNEIGH": true, + "RTM_GETNEIGHTBL": true, + "RTM_GETQDISC": true, + "RTM_GETROUTE": true, + "RTM_GETRULE": true, + "RTM_GETTCLASS": true, + "RTM_GETTFILTER": true, + "RTM_IEEE80211": true, + "RTM_IFANNOUNCE": true, + "RTM_IFINFO": true, + "RTM_IFINFO2": true, + "RTM_LLINFO_UPD": true, + "RTM_LOCK": true, + "RTM_LOSING": true, + "RTM_MAX": true, + "RTM_MAXSIZE": true, + "RTM_MISS": true, + "RTM_NEWACTION": true, + "RTM_NEWADDR": true, + "RTM_NEWADDRLABEL": true, + "RTM_NEWLINK": true, + "RTM_NEWMADDR": true, + "RTM_NEWMADDR2": true, + "RTM_NEWNDUSEROPT": true, + "RTM_NEWNEIGH": true, + "RTM_NEWNEIGHTBL": true, + "RTM_NEWPREFIX": true, + "RTM_NEWQDISC": true, + "RTM_NEWROUTE": true, + "RTM_NEWRULE": true, + "RTM_NEWTCLASS": true, + "RTM_NEWTFILTER": true, + "RTM_NR_FAMILIES": true, + "RTM_NR_MSGTYPES": true, + "RTM_OIFINFO": true, + "RTM_OLDADD": true, + "RTM_OLDDEL": true, + "RTM_OOIFINFO": true, + "RTM_REDIRECT": true, + "RTM_RESOLVE": true, + "RTM_RTTUNIT": true, + "RTM_SETDCB": true, + "RTM_SETGATE": true, + "RTM_SETLINK": true, + "RTM_SETNEIGHTBL": true, + "RTM_VERSION": true, + "RTNH_ALIGNTO": true, + "RTNH_F_DEAD": true, + "RTNH_F_ONLINK": true, + "RTNH_F_PERVASIVE": true, + "RTNLGRP_IPV4_IFADDR": true, + "RTNLGRP_IPV4_MROUTE": true, + "RTNLGRP_IPV4_ROUTE": true, + "RTNLGRP_IPV4_RULE": true, + "RTNLGRP_IPV6_IFADDR": true, + "RTNLGRP_IPV6_IFINFO": true, + "RTNLGRP_IPV6_MROUTE": true, + "RTNLGRP_IPV6_PREFIX": true, + "RTNLGRP_IPV6_ROUTE": true, + "RTNLGRP_IPV6_RULE": true, + "RTNLGRP_LINK": true, + "RTNLGRP_ND_USEROPT": true, + "RTNLGRP_NEIGH": true, + "RTNLGRP_NONE": true, + "RTNLGRP_NOTIFY": true, + "RTNLGRP_TC": true, + "RTN_ANYCAST": true, + "RTN_BLACKHOLE": true, + "RTN_BROADCAST": true, + "RTN_LOCAL": true, + "RTN_MAX": true, + "RTN_MULTICAST": true, + "RTN_NAT": true, + "RTN_PROHIBIT": true, + "RTN_THROW": true, + "RTN_UNICAST": true, + "RTN_UNREACHABLE": true, + "RTN_UNSPEC": true, + "RTN_XRESOLVE": true, + "RTPROT_BIRD": true, + "RTPROT_BOOT": true, + "RTPROT_DHCP": true, + "RTPROT_DNROUTED": true, + "RTPROT_GATED": true, + "RTPROT_KERNEL": true, + "RTPROT_MRT": true, + "RTPROT_NTK": true, + "RTPROT_RA": true, + "RTPROT_REDIRECT": true, + "RTPROT_STATIC": true, + "RTPROT_UNSPEC": true, + "RTPROT_XORP": true, + "RTPROT_ZEBRA": true, + "RTV_EXPIRE": true, + "RTV_HOPCOUNT": true, + "RTV_MTU": true, + "RTV_RPIPE": true, + "RTV_RTT": true, + "RTV_RTTVAR": true, + "RTV_SPIPE": true, + "RTV_SSTHRESH": true, + "RTV_WEIGHT": true, + "RT_CACHING_CONTEXT": true, + "RT_CLASS_DEFAULT": true, + "RT_CLASS_LOCAL": true, + "RT_CLASS_MAIN": true, + "RT_CLASS_MAX": true, + "RT_CLASS_UNSPEC": true, + "RT_DEFAULT_FIB": true, + "RT_NORTREF": true, + "RT_SCOPE_HOST": true, + "RT_SCOPE_LINK": true, + "RT_SCOPE_NOWHERE": true, + "RT_SCOPE_SITE": true, + "RT_SCOPE_UNIVERSE": true, + "RT_TABLEID_MAX": true, + "RT_TABLE_COMPAT": true, + "RT_TABLE_DEFAULT": true, + "RT_TABLE_LOCAL": true, + "RT_TABLE_MAIN": true, + "RT_TABLE_MAX": true, + "RT_TABLE_UNSPEC": true, + "RUSAGE_CHILDREN": true, + "RUSAGE_SELF": true, + "RUSAGE_THREAD": true, + "Radvisory_t": true, + "RawConn": true, + "RawSockaddr": true, + "RawSockaddrAny": true, + "RawSockaddrDatalink": true, + "RawSockaddrInet4": true, + "RawSockaddrInet6": true, + "RawSockaddrLinklayer": true, + "RawSockaddrNetlink": true, + "RawSockaddrUnix": true, + "RawSyscall": true, + "RawSyscall6": true, + "Read": true, + "ReadConsole": true, + "ReadDirectoryChanges": true, + "ReadDirent": true, + "ReadFile": true, + "Readlink": true, + "Reboot": true, + "Recvfrom": true, + "Recvmsg": true, + "RegCloseKey": true, + "RegEnumKeyEx": true, + "RegOpenKeyEx": true, + "RegQueryInfoKey": true, + "RegQueryValueEx": true, + "RemoveDirectory": true, + "Removexattr": true, + "Rename": true, + "Renameat": true, + "Revoke": true, + "Rlimit": true, + "Rmdir": true, + "RouteMessage": true, + "RouteRIB": true, + "RtAttr": true, + "RtGenmsg": true, + "RtMetrics": true, + "RtMsg": true, + "RtMsghdr": true, + "RtNexthop": true, + "Rusage": true, + "SCM_BINTIME": true, + "SCM_CREDENTIALS": true, + "SCM_CREDS": true, + "SCM_RIGHTS": true, + "SCM_TIMESTAMP": true, + "SCM_TIMESTAMPING": true, + "SCM_TIMESTAMPNS": true, + "SCM_TIMESTAMP_MONOTONIC": true, + "SHUT_RD": true, + "SHUT_RDWR": true, + "SHUT_WR": true, + "SID": true, + "SIDAndAttributes": true, + "SIGABRT": true, + "SIGALRM": true, + "SIGBUS": true, + "SIGCHLD": true, + "SIGCLD": true, + "SIGCONT": true, + "SIGEMT": true, + "SIGFPE": true, + "SIGHUP": true, + "SIGILL": true, + "SIGINFO": true, + "SIGINT": true, + "SIGIO": true, + "SIGIOT": true, + "SIGKILL": true, + "SIGLIBRT": true, + "SIGLWP": true, + "SIGPIPE": true, + "SIGPOLL": true, + "SIGPROF": true, + "SIGPWR": true, + "SIGQUIT": true, + "SIGSEGV": true, + "SIGSTKFLT": true, + "SIGSTOP": true, + "SIGSYS": true, + "SIGTERM": true, + "SIGTHR": true, + "SIGTRAP": true, + "SIGTSTP": true, + "SIGTTIN": true, + "SIGTTOU": true, + "SIGUNUSED": true, + "SIGURG": true, + "SIGUSR1": true, + "SIGUSR2": true, + "SIGVTALRM": true, + "SIGWINCH": true, + "SIGXCPU": true, + "SIGXFSZ": true, + "SIOCADDDLCI": true, + "SIOCADDMULTI": true, + "SIOCADDRT": true, + "SIOCAIFADDR": true, + "SIOCAIFGROUP": true, + "SIOCALIFADDR": true, + "SIOCARPIPLL": true, + "SIOCATMARK": true, + "SIOCAUTOADDR": true, + "SIOCAUTONETMASK": true, + "SIOCBRDGADD": true, + "SIOCBRDGADDS": true, + "SIOCBRDGARL": true, + "SIOCBRDGDADDR": true, + "SIOCBRDGDEL": true, + "SIOCBRDGDELS": true, + "SIOCBRDGFLUSH": true, + "SIOCBRDGFRL": true, + "SIOCBRDGGCACHE": true, + "SIOCBRDGGFD": true, + "SIOCBRDGGHT": true, + "SIOCBRDGGIFFLGS": true, + "SIOCBRDGGMA": true, + "SIOCBRDGGPARAM": true, + "SIOCBRDGGPRI": true, + "SIOCBRDGGRL": true, + "SIOCBRDGGSIFS": true, + "SIOCBRDGGTO": true, + "SIOCBRDGIFS": true, + "SIOCBRDGRTS": true, + "SIOCBRDGSADDR": true, + "SIOCBRDGSCACHE": true, + "SIOCBRDGSFD": true, + "SIOCBRDGSHT": true, + "SIOCBRDGSIFCOST": true, + "SIOCBRDGSIFFLGS": true, + "SIOCBRDGSIFPRIO": true, + "SIOCBRDGSMA": true, + "SIOCBRDGSPRI": true, + "SIOCBRDGSPROTO": true, + "SIOCBRDGSTO": true, + "SIOCBRDGSTXHC": true, + "SIOCDARP": true, + "SIOCDELDLCI": true, + "SIOCDELMULTI": true, + "SIOCDELRT": true, + "SIOCDEVPRIVATE": true, + "SIOCDIFADDR": true, + "SIOCDIFGROUP": true, + "SIOCDIFPHYADDR": true, + "SIOCDLIFADDR": true, + "SIOCDRARP": true, + "SIOCGARP": true, + "SIOCGDRVSPEC": true, + "SIOCGETKALIVE": true, + "SIOCGETLABEL": true, + "SIOCGETPFLOW": true, + "SIOCGETPFSYNC": true, + "SIOCGETSGCNT": true, + "SIOCGETVIFCNT": true, + "SIOCGETVLAN": true, + "SIOCGHIWAT": true, + "SIOCGIFADDR": true, + "SIOCGIFADDRPREF": true, + "SIOCGIFALIAS": true, + "SIOCGIFALTMTU": true, + "SIOCGIFASYNCMAP": true, + "SIOCGIFBOND": true, + "SIOCGIFBR": true, + "SIOCGIFBRDADDR": true, + "SIOCGIFCAP": true, + "SIOCGIFCONF": true, + "SIOCGIFCOUNT": true, + "SIOCGIFDATA": true, + "SIOCGIFDESCR": true, + "SIOCGIFDEVMTU": true, + "SIOCGIFDLT": true, + "SIOCGIFDSTADDR": true, + "SIOCGIFENCAP": true, + "SIOCGIFFIB": true, + "SIOCGIFFLAGS": true, + "SIOCGIFGATTR": true, + "SIOCGIFGENERIC": true, + "SIOCGIFGMEMB": true, + "SIOCGIFGROUP": true, + "SIOCGIFHARDMTU": true, + "SIOCGIFHWADDR": true, + "SIOCGIFINDEX": true, + "SIOCGIFKPI": true, + "SIOCGIFMAC": true, + "SIOCGIFMAP": true, + "SIOCGIFMEDIA": true, + "SIOCGIFMEM": true, + "SIOCGIFMETRIC": true, + "SIOCGIFMTU": true, + "SIOCGIFNAME": true, + "SIOCGIFNETMASK": true, + "SIOCGIFPDSTADDR": true, + "SIOCGIFPFLAGS": true, + "SIOCGIFPHYS": true, + "SIOCGIFPRIORITY": true, + "SIOCGIFPSRCADDR": true, + "SIOCGIFRDOMAIN": true, + "SIOCGIFRTLABEL": true, + "SIOCGIFSLAVE": true, + "SIOCGIFSTATUS": true, + "SIOCGIFTIMESLOT": true, + "SIOCGIFTXQLEN": true, + "SIOCGIFVLAN": true, + "SIOCGIFWAKEFLAGS": true, + "SIOCGIFXFLAGS": true, + "SIOCGLIFADDR": true, + "SIOCGLIFPHYADDR": true, + "SIOCGLIFPHYRTABLE": true, + "SIOCGLIFPHYTTL": true, + "SIOCGLINKSTR": true, + "SIOCGLOWAT": true, + "SIOCGPGRP": true, + "SIOCGPRIVATE_0": true, + "SIOCGPRIVATE_1": true, + "SIOCGRARP": true, + "SIOCGSPPPPARAMS": true, + "SIOCGSTAMP": true, + "SIOCGSTAMPNS": true, + "SIOCGVH": true, + "SIOCGVNETID": true, + "SIOCIFCREATE": true, + "SIOCIFCREATE2": true, + "SIOCIFDESTROY": true, + "SIOCIFGCLONERS": true, + "SIOCINITIFADDR": true, + "SIOCPROTOPRIVATE": true, + "SIOCRSLVMULTI": true, + "SIOCRTMSG": true, + "SIOCSARP": true, + "SIOCSDRVSPEC": true, + "SIOCSETKALIVE": true, + "SIOCSETLABEL": true, + "SIOCSETPFLOW": true, + "SIOCSETPFSYNC": true, + "SIOCSETVLAN": true, + "SIOCSHIWAT": true, + "SIOCSIFADDR": true, + "SIOCSIFADDRPREF": true, + "SIOCSIFALTMTU": true, + "SIOCSIFASYNCMAP": true, + "SIOCSIFBOND": true, + "SIOCSIFBR": true, + "SIOCSIFBRDADDR": true, + "SIOCSIFCAP": true, + "SIOCSIFDESCR": true, + "SIOCSIFDSTADDR": true, + "SIOCSIFENCAP": true, + "SIOCSIFFIB": true, + "SIOCSIFFLAGS": true, + "SIOCSIFGATTR": true, + "SIOCSIFGENERIC": true, + "SIOCSIFHWADDR": true, + "SIOCSIFHWBROADCAST": true, + "SIOCSIFKPI": true, + "SIOCSIFLINK": true, + "SIOCSIFLLADDR": true, + "SIOCSIFMAC": true, + "SIOCSIFMAP": true, + "SIOCSIFMEDIA": true, + "SIOCSIFMEM": true, + "SIOCSIFMETRIC": true, + "SIOCSIFMTU": true, + "SIOCSIFNAME": true, + "SIOCSIFNETMASK": true, + "SIOCSIFPFLAGS": true, + "SIOCSIFPHYADDR": true, + "SIOCSIFPHYS": true, + "SIOCSIFPRIORITY": true, + "SIOCSIFRDOMAIN": true, + "SIOCSIFRTLABEL": true, + "SIOCSIFRVNET": true, + "SIOCSIFSLAVE": true, + "SIOCSIFTIMESLOT": true, + "SIOCSIFTXQLEN": true, + "SIOCSIFVLAN": true, + "SIOCSIFVNET": true, + "SIOCSIFXFLAGS": true, + "SIOCSLIFPHYADDR": true, + "SIOCSLIFPHYRTABLE": true, + "SIOCSLIFPHYTTL": true, + "SIOCSLINKSTR": true, + "SIOCSLOWAT": true, + "SIOCSPGRP": true, + "SIOCSRARP": true, + "SIOCSSPPPPARAMS": true, + "SIOCSVH": true, + "SIOCSVNETID": true, + "SIOCZIFDATA": true, + "SIO_GET_EXTENSION_FUNCTION_POINTER": true, + "SIO_GET_INTERFACE_LIST": true, + "SIO_KEEPALIVE_VALS": true, + "SIO_UDP_CONNRESET": true, + "SOCK_CLOEXEC": true, + "SOCK_DCCP": true, + "SOCK_DGRAM": true, + "SOCK_FLAGS_MASK": true, + "SOCK_MAXADDRLEN": true, + "SOCK_NONBLOCK": true, + "SOCK_NOSIGPIPE": true, + "SOCK_PACKET": true, + "SOCK_RAW": true, + "SOCK_RDM": true, + "SOCK_SEQPACKET": true, + "SOCK_STREAM": true, + "SOL_AAL": true, + "SOL_ATM": true, + "SOL_DECNET": true, + "SOL_ICMPV6": true, + "SOL_IP": true, + "SOL_IPV6": true, + "SOL_IRDA": true, + "SOL_PACKET": true, + "SOL_RAW": true, + "SOL_SOCKET": true, + "SOL_TCP": true, + "SOL_X25": true, + "SOMAXCONN": true, + "SO_ACCEPTCONN": true, + "SO_ACCEPTFILTER": true, + "SO_ATTACH_FILTER": true, + "SO_BINDANY": true, + "SO_BINDTODEVICE": true, + "SO_BINTIME": true, + "SO_BROADCAST": true, + "SO_BSDCOMPAT": true, + "SO_DEBUG": true, + "SO_DETACH_FILTER": true, + "SO_DOMAIN": true, + "SO_DONTROUTE": true, + "SO_DONTTRUNC": true, + "SO_ERROR": true, + "SO_KEEPALIVE": true, + "SO_LABEL": true, + "SO_LINGER": true, + "SO_LINGER_SEC": true, + "SO_LISTENINCQLEN": true, + "SO_LISTENQLEN": true, + "SO_LISTENQLIMIT": true, + "SO_MARK": true, + "SO_NETPROC": true, + "SO_NKE": true, + "SO_NOADDRERR": true, + "SO_NOHEADER": true, + "SO_NOSIGPIPE": true, + "SO_NOTIFYCONFLICT": true, + "SO_NO_CHECK": true, + "SO_NO_DDP": true, + "SO_NO_OFFLOAD": true, + "SO_NP_EXTENSIONS": true, + "SO_NREAD": true, + "SO_NWRITE": true, + "SO_OOBINLINE": true, + "SO_OVERFLOWED": true, + "SO_PASSCRED": true, + "SO_PASSSEC": true, + "SO_PEERCRED": true, + "SO_PEERLABEL": true, + "SO_PEERNAME": true, + "SO_PEERSEC": true, + "SO_PRIORITY": true, + "SO_PROTOCOL": true, + "SO_PROTOTYPE": true, + "SO_RANDOMPORT": true, + "SO_RCVBUF": true, + "SO_RCVBUFFORCE": true, + "SO_RCVLOWAT": true, + "SO_RCVTIMEO": true, + "SO_RESTRICTIONS": true, + "SO_RESTRICT_DENYIN": true, + "SO_RESTRICT_DENYOUT": true, + "SO_RESTRICT_DENYSET": true, + "SO_REUSEADDR": true, + "SO_REUSEPORT": true, + "SO_REUSESHAREUID": true, + "SO_RTABLE": true, + "SO_RXQ_OVFL": true, + "SO_SECURITY_AUTHENTICATION": true, + "SO_SECURITY_ENCRYPTION_NETWORK": true, + "SO_SECURITY_ENCRYPTION_TRANSPORT": true, + "SO_SETFIB": true, + "SO_SNDBUF": true, + "SO_SNDBUFFORCE": true, + "SO_SNDLOWAT": true, + "SO_SNDTIMEO": true, + "SO_SPLICE": true, + "SO_TIMESTAMP": true, + "SO_TIMESTAMPING": true, + "SO_TIMESTAMPNS": true, + "SO_TIMESTAMP_MONOTONIC": true, + "SO_TYPE": true, + "SO_UPCALLCLOSEWAIT": true, + "SO_UPDATE_ACCEPT_CONTEXT": true, + "SO_UPDATE_CONNECT_CONTEXT": true, + "SO_USELOOPBACK": true, + "SO_USER_COOKIE": true, + "SO_VENDOR": true, + "SO_WANTMORE": true, + "SO_WANTOOBFLAG": true, + "SSLExtraCertChainPolicyPara": true, + "STANDARD_RIGHTS_ALL": true, + "STANDARD_RIGHTS_EXECUTE": true, + "STANDARD_RIGHTS_READ": true, + "STANDARD_RIGHTS_REQUIRED": true, + "STANDARD_RIGHTS_WRITE": true, + "STARTF_USESHOWWINDOW": true, + "STARTF_USESTDHANDLES": true, + "STD_ERROR_HANDLE": true, + "STD_INPUT_HANDLE": true, + "STD_OUTPUT_HANDLE": true, + "SUBLANG_ENGLISH_US": true, + "SW_FORCEMINIMIZE": true, + "SW_HIDE": true, + "SW_MAXIMIZE": true, + "SW_MINIMIZE": true, + "SW_NORMAL": true, + "SW_RESTORE": true, + "SW_SHOW": true, + "SW_SHOWDEFAULT": true, + "SW_SHOWMAXIMIZED": true, + "SW_SHOWMINIMIZED": true, + "SW_SHOWMINNOACTIVE": true, + "SW_SHOWNA": true, + "SW_SHOWNOACTIVATE": true, + "SW_SHOWNORMAL": true, + "SYMBOLIC_LINK_FLAG_DIRECTORY": true, + "SYNCHRONIZE": true, + "SYSCTL_VERSION": true, + "SYSCTL_VERS_0": true, + "SYSCTL_VERS_1": true, + "SYSCTL_VERS_MASK": true, + "SYS_ABORT2": true, + "SYS_ACCEPT": true, + "SYS_ACCEPT4": true, + "SYS_ACCEPT_NOCANCEL": true, + "SYS_ACCESS": true, + "SYS_ACCESS_EXTENDED": true, + "SYS_ACCT": true, + "SYS_ADD_KEY": true, + "SYS_ADD_PROFIL": true, + "SYS_ADJFREQ": true, + "SYS_ADJTIME": true, + "SYS_ADJTIMEX": true, + "SYS_AFS_SYSCALL": true, + "SYS_AIO_CANCEL": true, + "SYS_AIO_ERROR": true, + "SYS_AIO_FSYNC": true, + "SYS_AIO_READ": true, + "SYS_AIO_RETURN": true, + "SYS_AIO_SUSPEND": true, + "SYS_AIO_SUSPEND_NOCANCEL": true, + "SYS_AIO_WRITE": true, + "SYS_ALARM": true, + "SYS_ARCH_PRCTL": true, + "SYS_ARM_FADVISE64_64": true, + "SYS_ARM_SYNC_FILE_RANGE": true, + "SYS_ATGETMSG": true, + "SYS_ATPGETREQ": true, + "SYS_ATPGETRSP": true, + "SYS_ATPSNDREQ": true, + "SYS_ATPSNDRSP": true, + "SYS_ATPUTMSG": true, + "SYS_ATSOCKET": true, + "SYS_AUDIT": true, + "SYS_AUDITCTL": true, + "SYS_AUDITON": true, + "SYS_AUDIT_SESSION_JOIN": true, + "SYS_AUDIT_SESSION_PORT": true, + "SYS_AUDIT_SESSION_SELF": true, + "SYS_BDFLUSH": true, + "SYS_BIND": true, + "SYS_BINDAT": true, + "SYS_BREAK": true, + "SYS_BRK": true, + "SYS_BSDTHREAD_CREATE": true, + "SYS_BSDTHREAD_REGISTER": true, + "SYS_BSDTHREAD_TERMINATE": true, + "SYS_CAPGET": true, + "SYS_CAPSET": true, + "SYS_CAP_ENTER": true, + "SYS_CAP_FCNTLS_GET": true, + "SYS_CAP_FCNTLS_LIMIT": true, + "SYS_CAP_GETMODE": true, + "SYS_CAP_GETRIGHTS": true, + "SYS_CAP_IOCTLS_GET": true, + "SYS_CAP_IOCTLS_LIMIT": true, + "SYS_CAP_NEW": true, + "SYS_CAP_RIGHTS_GET": true, + "SYS_CAP_RIGHTS_LIMIT": true, + "SYS_CHDIR": true, + "SYS_CHFLAGS": true, + "SYS_CHFLAGSAT": true, + "SYS_CHMOD": true, + "SYS_CHMOD_EXTENDED": true, + "SYS_CHOWN": true, + "SYS_CHOWN32": true, + "SYS_CHROOT": true, + "SYS_CHUD": true, + "SYS_CLOCK_ADJTIME": true, + "SYS_CLOCK_GETCPUCLOCKID2": true, + "SYS_CLOCK_GETRES": true, + "SYS_CLOCK_GETTIME": true, + "SYS_CLOCK_NANOSLEEP": true, + "SYS_CLOCK_SETTIME": true, + "SYS_CLONE": true, + "SYS_CLOSE": true, + "SYS_CLOSEFROM": true, + "SYS_CLOSE_NOCANCEL": true, + "SYS_CONNECT": true, + "SYS_CONNECTAT": true, + "SYS_CONNECT_NOCANCEL": true, + "SYS_COPYFILE": true, + "SYS_CPUSET": true, + "SYS_CPUSET_GETAFFINITY": true, + "SYS_CPUSET_GETID": true, + "SYS_CPUSET_SETAFFINITY": true, + "SYS_CPUSET_SETID": true, + "SYS_CREAT": true, + "SYS_CREATE_MODULE": true, + "SYS_CSOPS": true, + "SYS_DELETE": true, + "SYS_DELETE_MODULE": true, + "SYS_DUP": true, + "SYS_DUP2": true, + "SYS_DUP3": true, + "SYS_EACCESS": true, + "SYS_EPOLL_CREATE": true, + "SYS_EPOLL_CREATE1": true, + "SYS_EPOLL_CTL": true, + "SYS_EPOLL_CTL_OLD": true, + "SYS_EPOLL_PWAIT": true, + "SYS_EPOLL_WAIT": true, + "SYS_EPOLL_WAIT_OLD": true, + "SYS_EVENTFD": true, + "SYS_EVENTFD2": true, + "SYS_EXCHANGEDATA": true, + "SYS_EXECVE": true, + "SYS_EXIT": true, + "SYS_EXIT_GROUP": true, + "SYS_EXTATTRCTL": true, + "SYS_EXTATTR_DELETE_FD": true, + "SYS_EXTATTR_DELETE_FILE": true, + "SYS_EXTATTR_DELETE_LINK": true, + "SYS_EXTATTR_GET_FD": true, + "SYS_EXTATTR_GET_FILE": true, + "SYS_EXTATTR_GET_LINK": true, + "SYS_EXTATTR_LIST_FD": true, + "SYS_EXTATTR_LIST_FILE": true, + "SYS_EXTATTR_LIST_LINK": true, + "SYS_EXTATTR_SET_FD": true, + "SYS_EXTATTR_SET_FILE": true, + "SYS_EXTATTR_SET_LINK": true, + "SYS_FACCESSAT": true, + "SYS_FADVISE64": true, + "SYS_FADVISE64_64": true, + "SYS_FALLOCATE": true, + "SYS_FANOTIFY_INIT": true, + "SYS_FANOTIFY_MARK": true, + "SYS_FCHDIR": true, + "SYS_FCHFLAGS": true, + "SYS_FCHMOD": true, + "SYS_FCHMODAT": true, + "SYS_FCHMOD_EXTENDED": true, + "SYS_FCHOWN": true, + "SYS_FCHOWN32": true, + "SYS_FCHOWNAT": true, + "SYS_FCHROOT": true, + "SYS_FCNTL": true, + "SYS_FCNTL64": true, + "SYS_FCNTL_NOCANCEL": true, + "SYS_FDATASYNC": true, + "SYS_FEXECVE": true, + "SYS_FFCLOCK_GETCOUNTER": true, + "SYS_FFCLOCK_GETESTIMATE": true, + "SYS_FFCLOCK_SETESTIMATE": true, + "SYS_FFSCTL": true, + "SYS_FGETATTRLIST": true, + "SYS_FGETXATTR": true, + "SYS_FHOPEN": true, + "SYS_FHSTAT": true, + "SYS_FHSTATFS": true, + "SYS_FILEPORT_MAKEFD": true, + "SYS_FILEPORT_MAKEPORT": true, + "SYS_FKTRACE": true, + "SYS_FLISTXATTR": true, + "SYS_FLOCK": true, + "SYS_FORK": true, + "SYS_FPATHCONF": true, + "SYS_FREEBSD6_FTRUNCATE": true, + "SYS_FREEBSD6_LSEEK": true, + "SYS_FREEBSD6_MMAP": true, + "SYS_FREEBSD6_PREAD": true, + "SYS_FREEBSD6_PWRITE": true, + "SYS_FREEBSD6_TRUNCATE": true, + "SYS_FREMOVEXATTR": true, + "SYS_FSCTL": true, + "SYS_FSETATTRLIST": true, + "SYS_FSETXATTR": true, + "SYS_FSGETPATH": true, + "SYS_FSTAT": true, + "SYS_FSTAT64": true, + "SYS_FSTAT64_EXTENDED": true, + "SYS_FSTATAT": true, + "SYS_FSTATAT64": true, + "SYS_FSTATFS": true, + "SYS_FSTATFS64": true, + "SYS_FSTATV": true, + "SYS_FSTATVFS1": true, + "SYS_FSTAT_EXTENDED": true, + "SYS_FSYNC": true, + "SYS_FSYNC_NOCANCEL": true, + "SYS_FSYNC_RANGE": true, + "SYS_FTIME": true, + "SYS_FTRUNCATE": true, + "SYS_FTRUNCATE64": true, + "SYS_FUTEX": true, + "SYS_FUTIMENS": true, + "SYS_FUTIMES": true, + "SYS_FUTIMESAT": true, + "SYS_GETATTRLIST": true, + "SYS_GETAUDIT": true, + "SYS_GETAUDIT_ADDR": true, + "SYS_GETAUID": true, + "SYS_GETCONTEXT": true, + "SYS_GETCPU": true, + "SYS_GETCWD": true, + "SYS_GETDENTS": true, + "SYS_GETDENTS64": true, + "SYS_GETDIRENTRIES": true, + "SYS_GETDIRENTRIES64": true, + "SYS_GETDIRENTRIESATTR": true, + "SYS_GETDTABLECOUNT": true, + "SYS_GETDTABLESIZE": true, + "SYS_GETEGID": true, + "SYS_GETEGID32": true, + "SYS_GETEUID": true, + "SYS_GETEUID32": true, + "SYS_GETFH": true, + "SYS_GETFSSTAT": true, + "SYS_GETFSSTAT64": true, + "SYS_GETGID": true, + "SYS_GETGID32": true, + "SYS_GETGROUPS": true, + "SYS_GETGROUPS32": true, + "SYS_GETHOSTUUID": true, + "SYS_GETITIMER": true, + "SYS_GETLCID": true, + "SYS_GETLOGIN": true, + "SYS_GETLOGINCLASS": true, + "SYS_GETPEERNAME": true, + "SYS_GETPGID": true, + "SYS_GETPGRP": true, + "SYS_GETPID": true, + "SYS_GETPMSG": true, + "SYS_GETPPID": true, + "SYS_GETPRIORITY": true, + "SYS_GETRESGID": true, + "SYS_GETRESGID32": true, + "SYS_GETRESUID": true, + "SYS_GETRESUID32": true, + "SYS_GETRLIMIT": true, + "SYS_GETRTABLE": true, + "SYS_GETRUSAGE": true, + "SYS_GETSGROUPS": true, + "SYS_GETSID": true, + "SYS_GETSOCKNAME": true, + "SYS_GETSOCKOPT": true, + "SYS_GETTHRID": true, + "SYS_GETTID": true, + "SYS_GETTIMEOFDAY": true, + "SYS_GETUID": true, + "SYS_GETUID32": true, + "SYS_GETVFSSTAT": true, + "SYS_GETWGROUPS": true, + "SYS_GETXATTR": true, + "SYS_GET_KERNEL_SYMS": true, + "SYS_GET_MEMPOLICY": true, + "SYS_GET_ROBUST_LIST": true, + "SYS_GET_THREAD_AREA": true, + "SYS_GTTY": true, + "SYS_IDENTITYSVC": true, + "SYS_IDLE": true, + "SYS_INITGROUPS": true, + "SYS_INIT_MODULE": true, + "SYS_INOTIFY_ADD_WATCH": true, + "SYS_INOTIFY_INIT": true, + "SYS_INOTIFY_INIT1": true, + "SYS_INOTIFY_RM_WATCH": true, + "SYS_IOCTL": true, + "SYS_IOPERM": true, + "SYS_IOPL": true, + "SYS_IOPOLICYSYS": true, + "SYS_IOPRIO_GET": true, + "SYS_IOPRIO_SET": true, + "SYS_IO_CANCEL": true, + "SYS_IO_DESTROY": true, + "SYS_IO_GETEVENTS": true, + "SYS_IO_SETUP": true, + "SYS_IO_SUBMIT": true, + "SYS_IPC": true, + "SYS_ISSETUGID": true, + "SYS_JAIL": true, + "SYS_JAIL_ATTACH": true, + "SYS_JAIL_GET": true, + "SYS_JAIL_REMOVE": true, + "SYS_JAIL_SET": true, + "SYS_KDEBUG_TRACE": true, + "SYS_KENV": true, + "SYS_KEVENT": true, + "SYS_KEVENT64": true, + "SYS_KEXEC_LOAD": true, + "SYS_KEYCTL": true, + "SYS_KILL": true, + "SYS_KLDFIND": true, + "SYS_KLDFIRSTMOD": true, + "SYS_KLDLOAD": true, + "SYS_KLDNEXT": true, + "SYS_KLDSTAT": true, + "SYS_KLDSYM": true, + "SYS_KLDUNLOAD": true, + "SYS_KLDUNLOADF": true, + "SYS_KQUEUE": true, + "SYS_KQUEUE1": true, + "SYS_KTIMER_CREATE": true, + "SYS_KTIMER_DELETE": true, + "SYS_KTIMER_GETOVERRUN": true, + "SYS_KTIMER_GETTIME": true, + "SYS_KTIMER_SETTIME": true, + "SYS_KTRACE": true, + "SYS_LCHFLAGS": true, + "SYS_LCHMOD": true, + "SYS_LCHOWN": true, + "SYS_LCHOWN32": true, + "SYS_LGETFH": true, + "SYS_LGETXATTR": true, + "SYS_LINK": true, + "SYS_LINKAT": true, + "SYS_LIO_LISTIO": true, + "SYS_LISTEN": true, + "SYS_LISTXATTR": true, + "SYS_LLISTXATTR": true, + "SYS_LOCK": true, + "SYS_LOOKUP_DCOOKIE": true, + "SYS_LPATHCONF": true, + "SYS_LREMOVEXATTR": true, + "SYS_LSEEK": true, + "SYS_LSETXATTR": true, + "SYS_LSTAT": true, + "SYS_LSTAT64": true, + "SYS_LSTAT64_EXTENDED": true, + "SYS_LSTATV": true, + "SYS_LSTAT_EXTENDED": true, + "SYS_LUTIMES": true, + "SYS_MAC_SYSCALL": true, + "SYS_MADVISE": true, + "SYS_MADVISE1": true, + "SYS_MAXSYSCALL": true, + "SYS_MBIND": true, + "SYS_MIGRATE_PAGES": true, + "SYS_MINCORE": true, + "SYS_MINHERIT": true, + "SYS_MKCOMPLEX": true, + "SYS_MKDIR": true, + "SYS_MKDIRAT": true, + "SYS_MKDIR_EXTENDED": true, + "SYS_MKFIFO": true, + "SYS_MKFIFOAT": true, + "SYS_MKFIFO_EXTENDED": true, + "SYS_MKNOD": true, + "SYS_MKNODAT": true, + "SYS_MLOCK": true, + "SYS_MLOCKALL": true, + "SYS_MMAP": true, + "SYS_MMAP2": true, + "SYS_MODCTL": true, + "SYS_MODFIND": true, + "SYS_MODFNEXT": true, + "SYS_MODIFY_LDT": true, + "SYS_MODNEXT": true, + "SYS_MODSTAT": true, + "SYS_MODWATCH": true, + "SYS_MOUNT": true, + "SYS_MOVE_PAGES": true, + "SYS_MPROTECT": true, + "SYS_MPX": true, + "SYS_MQUERY": true, + "SYS_MQ_GETSETATTR": true, + "SYS_MQ_NOTIFY": true, + "SYS_MQ_OPEN": true, + "SYS_MQ_TIMEDRECEIVE": true, + "SYS_MQ_TIMEDSEND": true, + "SYS_MQ_UNLINK": true, + "SYS_MREMAP": true, + "SYS_MSGCTL": true, + "SYS_MSGGET": true, + "SYS_MSGRCV": true, + "SYS_MSGRCV_NOCANCEL": true, + "SYS_MSGSND": true, + "SYS_MSGSND_NOCANCEL": true, + "SYS_MSGSYS": true, + "SYS_MSYNC": true, + "SYS_MSYNC_NOCANCEL": true, + "SYS_MUNLOCK": true, + "SYS_MUNLOCKALL": true, + "SYS_MUNMAP": true, + "SYS_NAME_TO_HANDLE_AT": true, + "SYS_NANOSLEEP": true, + "SYS_NEWFSTATAT": true, + "SYS_NFSCLNT": true, + "SYS_NFSSERVCTL": true, + "SYS_NFSSVC": true, + "SYS_NFSTAT": true, + "SYS_NICE": true, + "SYS_NLSTAT": true, + "SYS_NMOUNT": true, + "SYS_NSTAT": true, + "SYS_NTP_ADJTIME": true, + "SYS_NTP_GETTIME": true, + "SYS_OABI_SYSCALL_BASE": true, + "SYS_OBREAK": true, + "SYS_OLDFSTAT": true, + "SYS_OLDLSTAT": true, + "SYS_OLDOLDUNAME": true, + "SYS_OLDSTAT": true, + "SYS_OLDUNAME": true, + "SYS_OPEN": true, + "SYS_OPENAT": true, + "SYS_OPENBSD_POLL": true, + "SYS_OPEN_BY_HANDLE_AT": true, + "SYS_OPEN_EXTENDED": true, + "SYS_OPEN_NOCANCEL": true, + "SYS_OVADVISE": true, + "SYS_PACCEPT": true, + "SYS_PATHCONF": true, + "SYS_PAUSE": true, + "SYS_PCICONFIG_IOBASE": true, + "SYS_PCICONFIG_READ": true, + "SYS_PCICONFIG_WRITE": true, + "SYS_PDFORK": true, + "SYS_PDGETPID": true, + "SYS_PDKILL": true, + "SYS_PERF_EVENT_OPEN": true, + "SYS_PERSONALITY": true, + "SYS_PID_HIBERNATE": true, + "SYS_PID_RESUME": true, + "SYS_PID_SHUTDOWN_SOCKETS": true, + "SYS_PID_SUSPEND": true, + "SYS_PIPE": true, + "SYS_PIPE2": true, + "SYS_PIVOT_ROOT": true, + "SYS_PMC_CONTROL": true, + "SYS_PMC_GET_INFO": true, + "SYS_POLL": true, + "SYS_POLLTS": true, + "SYS_POLL_NOCANCEL": true, + "SYS_POSIX_FADVISE": true, + "SYS_POSIX_FALLOCATE": true, + "SYS_POSIX_OPENPT": true, + "SYS_POSIX_SPAWN": true, + "SYS_PPOLL": true, + "SYS_PRCTL": true, + "SYS_PREAD": true, + "SYS_PREAD64": true, + "SYS_PREADV": true, + "SYS_PREAD_NOCANCEL": true, + "SYS_PRLIMIT64": true, + "SYS_PROCCTL": true, + "SYS_PROCESS_POLICY": true, + "SYS_PROCESS_VM_READV": true, + "SYS_PROCESS_VM_WRITEV": true, + "SYS_PROC_INFO": true, + "SYS_PROF": true, + "SYS_PROFIL": true, + "SYS_PSELECT": true, + "SYS_PSELECT6": true, + "SYS_PSET_ASSIGN": true, + "SYS_PSET_CREATE": true, + "SYS_PSET_DESTROY": true, + "SYS_PSYNCH_CVBROAD": true, + "SYS_PSYNCH_CVCLRPREPOST": true, + "SYS_PSYNCH_CVSIGNAL": true, + "SYS_PSYNCH_CVWAIT": true, + "SYS_PSYNCH_MUTEXDROP": true, + "SYS_PSYNCH_MUTEXWAIT": true, + "SYS_PSYNCH_RW_DOWNGRADE": true, + "SYS_PSYNCH_RW_LONGRDLOCK": true, + "SYS_PSYNCH_RW_RDLOCK": true, + "SYS_PSYNCH_RW_UNLOCK": true, + "SYS_PSYNCH_RW_UNLOCK2": true, + "SYS_PSYNCH_RW_UPGRADE": true, + "SYS_PSYNCH_RW_WRLOCK": true, + "SYS_PSYNCH_RW_YIELDWRLOCK": true, + "SYS_PTRACE": true, + "SYS_PUTPMSG": true, + "SYS_PWRITE": true, + "SYS_PWRITE64": true, + "SYS_PWRITEV": true, + "SYS_PWRITE_NOCANCEL": true, + "SYS_QUERY_MODULE": true, + "SYS_QUOTACTL": true, + "SYS_RASCTL": true, + "SYS_RCTL_ADD_RULE": true, + "SYS_RCTL_GET_LIMITS": true, + "SYS_RCTL_GET_RACCT": true, + "SYS_RCTL_GET_RULES": true, + "SYS_RCTL_REMOVE_RULE": true, + "SYS_READ": true, + "SYS_READAHEAD": true, + "SYS_READDIR": true, + "SYS_READLINK": true, + "SYS_READLINKAT": true, + "SYS_READV": true, + "SYS_READV_NOCANCEL": true, + "SYS_READ_NOCANCEL": true, + "SYS_REBOOT": true, + "SYS_RECV": true, + "SYS_RECVFROM": true, + "SYS_RECVFROM_NOCANCEL": true, + "SYS_RECVMMSG": true, + "SYS_RECVMSG": true, + "SYS_RECVMSG_NOCANCEL": true, + "SYS_REMAP_FILE_PAGES": true, + "SYS_REMOVEXATTR": true, + "SYS_RENAME": true, + "SYS_RENAMEAT": true, + "SYS_REQUEST_KEY": true, + "SYS_RESTART_SYSCALL": true, + "SYS_REVOKE": true, + "SYS_RFORK": true, + "SYS_RMDIR": true, + "SYS_RTPRIO": true, + "SYS_RTPRIO_THREAD": true, + "SYS_RT_SIGACTION": true, + "SYS_RT_SIGPENDING": true, + "SYS_RT_SIGPROCMASK": true, + "SYS_RT_SIGQUEUEINFO": true, + "SYS_RT_SIGRETURN": true, + "SYS_RT_SIGSUSPEND": true, + "SYS_RT_SIGTIMEDWAIT": true, + "SYS_RT_TGSIGQUEUEINFO": true, + "SYS_SBRK": true, + "SYS_SCHED_GETAFFINITY": true, + "SYS_SCHED_GETPARAM": true, + "SYS_SCHED_GETSCHEDULER": true, + "SYS_SCHED_GET_PRIORITY_MAX": true, + "SYS_SCHED_GET_PRIORITY_MIN": true, + "SYS_SCHED_RR_GET_INTERVAL": true, + "SYS_SCHED_SETAFFINITY": true, + "SYS_SCHED_SETPARAM": true, + "SYS_SCHED_SETSCHEDULER": true, + "SYS_SCHED_YIELD": true, + "SYS_SCTP_GENERIC_RECVMSG": true, + "SYS_SCTP_GENERIC_SENDMSG": true, + "SYS_SCTP_GENERIC_SENDMSG_IOV": true, + "SYS_SCTP_PEELOFF": true, + "SYS_SEARCHFS": true, + "SYS_SECURITY": true, + "SYS_SELECT": true, + "SYS_SELECT_NOCANCEL": true, + "SYS_SEMCONFIG": true, + "SYS_SEMCTL": true, + "SYS_SEMGET": true, + "SYS_SEMOP": true, + "SYS_SEMSYS": true, + "SYS_SEMTIMEDOP": true, + "SYS_SEM_CLOSE": true, + "SYS_SEM_DESTROY": true, + "SYS_SEM_GETVALUE": true, + "SYS_SEM_INIT": true, + "SYS_SEM_OPEN": true, + "SYS_SEM_POST": true, + "SYS_SEM_TRYWAIT": true, + "SYS_SEM_UNLINK": true, + "SYS_SEM_WAIT": true, + "SYS_SEM_WAIT_NOCANCEL": true, + "SYS_SEND": true, + "SYS_SENDFILE": true, + "SYS_SENDFILE64": true, + "SYS_SENDMMSG": true, + "SYS_SENDMSG": true, + "SYS_SENDMSG_NOCANCEL": true, + "SYS_SENDTO": true, + "SYS_SENDTO_NOCANCEL": true, + "SYS_SETATTRLIST": true, + "SYS_SETAUDIT": true, + "SYS_SETAUDIT_ADDR": true, + "SYS_SETAUID": true, + "SYS_SETCONTEXT": true, + "SYS_SETDOMAINNAME": true, + "SYS_SETEGID": true, + "SYS_SETEUID": true, + "SYS_SETFIB": true, + "SYS_SETFSGID": true, + "SYS_SETFSGID32": true, + "SYS_SETFSUID": true, + "SYS_SETFSUID32": true, + "SYS_SETGID": true, + "SYS_SETGID32": true, + "SYS_SETGROUPS": true, + "SYS_SETGROUPS32": true, + "SYS_SETHOSTNAME": true, + "SYS_SETITIMER": true, + "SYS_SETLCID": true, + "SYS_SETLOGIN": true, + "SYS_SETLOGINCLASS": true, + "SYS_SETNS": true, + "SYS_SETPGID": true, + "SYS_SETPRIORITY": true, + "SYS_SETPRIVEXEC": true, + "SYS_SETREGID": true, + "SYS_SETREGID32": true, + "SYS_SETRESGID": true, + "SYS_SETRESGID32": true, + "SYS_SETRESUID": true, + "SYS_SETRESUID32": true, + "SYS_SETREUID": true, + "SYS_SETREUID32": true, + "SYS_SETRLIMIT": true, + "SYS_SETRTABLE": true, + "SYS_SETSGROUPS": true, + "SYS_SETSID": true, + "SYS_SETSOCKOPT": true, + "SYS_SETTID": true, + "SYS_SETTID_WITH_PID": true, + "SYS_SETTIMEOFDAY": true, + "SYS_SETUID": true, + "SYS_SETUID32": true, + "SYS_SETWGROUPS": true, + "SYS_SETXATTR": true, + "SYS_SET_MEMPOLICY": true, + "SYS_SET_ROBUST_LIST": true, + "SYS_SET_THREAD_AREA": true, + "SYS_SET_TID_ADDRESS": true, + "SYS_SGETMASK": true, + "SYS_SHARED_REGION_CHECK_NP": true, + "SYS_SHARED_REGION_MAP_AND_SLIDE_NP": true, + "SYS_SHMAT": true, + "SYS_SHMCTL": true, + "SYS_SHMDT": true, + "SYS_SHMGET": true, + "SYS_SHMSYS": true, + "SYS_SHM_OPEN": true, + "SYS_SHM_UNLINK": true, + "SYS_SHUTDOWN": true, + "SYS_SIGACTION": true, + "SYS_SIGALTSTACK": true, + "SYS_SIGNAL": true, + "SYS_SIGNALFD": true, + "SYS_SIGNALFD4": true, + "SYS_SIGPENDING": true, + "SYS_SIGPROCMASK": true, + "SYS_SIGQUEUE": true, + "SYS_SIGQUEUEINFO": true, + "SYS_SIGRETURN": true, + "SYS_SIGSUSPEND": true, + "SYS_SIGSUSPEND_NOCANCEL": true, + "SYS_SIGTIMEDWAIT": true, + "SYS_SIGWAIT": true, + "SYS_SIGWAITINFO": true, + "SYS_SOCKET": true, + "SYS_SOCKETCALL": true, + "SYS_SOCKETPAIR": true, + "SYS_SPLICE": true, + "SYS_SSETMASK": true, + "SYS_SSTK": true, + "SYS_STACK_SNAPSHOT": true, + "SYS_STAT": true, + "SYS_STAT64": true, + "SYS_STAT64_EXTENDED": true, + "SYS_STATFS": true, + "SYS_STATFS64": true, + "SYS_STATV": true, + "SYS_STATVFS1": true, + "SYS_STAT_EXTENDED": true, + "SYS_STIME": true, + "SYS_STTY": true, + "SYS_SWAPCONTEXT": true, + "SYS_SWAPCTL": true, + "SYS_SWAPOFF": true, + "SYS_SWAPON": true, + "SYS_SYMLINK": true, + "SYS_SYMLINKAT": true, + "SYS_SYNC": true, + "SYS_SYNCFS": true, + "SYS_SYNC_FILE_RANGE": true, + "SYS_SYSARCH": true, + "SYS_SYSCALL": true, + "SYS_SYSCALL_BASE": true, + "SYS_SYSFS": true, + "SYS_SYSINFO": true, + "SYS_SYSLOG": true, + "SYS_TEE": true, + "SYS_TGKILL": true, + "SYS_THREAD_SELFID": true, + "SYS_THR_CREATE": true, + "SYS_THR_EXIT": true, + "SYS_THR_KILL": true, + "SYS_THR_KILL2": true, + "SYS_THR_NEW": true, + "SYS_THR_SELF": true, + "SYS_THR_SET_NAME": true, + "SYS_THR_SUSPEND": true, + "SYS_THR_WAKE": true, + "SYS_TIME": true, + "SYS_TIMERFD_CREATE": true, + "SYS_TIMERFD_GETTIME": true, + "SYS_TIMERFD_SETTIME": true, + "SYS_TIMER_CREATE": true, + "SYS_TIMER_DELETE": true, + "SYS_TIMER_GETOVERRUN": true, + "SYS_TIMER_GETTIME": true, + "SYS_TIMER_SETTIME": true, + "SYS_TIMES": true, + "SYS_TKILL": true, + "SYS_TRUNCATE": true, + "SYS_TRUNCATE64": true, + "SYS_TUXCALL": true, + "SYS_UGETRLIMIT": true, + "SYS_ULIMIT": true, + "SYS_UMASK": true, + "SYS_UMASK_EXTENDED": true, + "SYS_UMOUNT": true, + "SYS_UMOUNT2": true, + "SYS_UNAME": true, + "SYS_UNDELETE": true, + "SYS_UNLINK": true, + "SYS_UNLINKAT": true, + "SYS_UNMOUNT": true, + "SYS_UNSHARE": true, + "SYS_USELIB": true, + "SYS_USTAT": true, + "SYS_UTIME": true, + "SYS_UTIMENSAT": true, + "SYS_UTIMES": true, + "SYS_UTRACE": true, + "SYS_UUIDGEN": true, + "SYS_VADVISE": true, + "SYS_VFORK": true, + "SYS_VHANGUP": true, + "SYS_VM86": true, + "SYS_VM86OLD": true, + "SYS_VMSPLICE": true, + "SYS_VM_PRESSURE_MONITOR": true, + "SYS_VSERVER": true, + "SYS_WAIT4": true, + "SYS_WAIT4_NOCANCEL": true, + "SYS_WAIT6": true, + "SYS_WAITEVENT": true, + "SYS_WAITID": true, + "SYS_WAITID_NOCANCEL": true, + "SYS_WAITPID": true, + "SYS_WATCHEVENT": true, + "SYS_WORKQ_KERNRETURN": true, + "SYS_WORKQ_OPEN": true, + "SYS_WRITE": true, + "SYS_WRITEV": true, + "SYS_WRITEV_NOCANCEL": true, + "SYS_WRITE_NOCANCEL": true, + "SYS_YIELD": true, + "SYS__LLSEEK": true, + "SYS__LWP_CONTINUE": true, + "SYS__LWP_CREATE": true, + "SYS__LWP_CTL": true, + "SYS__LWP_DETACH": true, + "SYS__LWP_EXIT": true, + "SYS__LWP_GETNAME": true, + "SYS__LWP_GETPRIVATE": true, + "SYS__LWP_KILL": true, + "SYS__LWP_PARK": true, + "SYS__LWP_SELF": true, + "SYS__LWP_SETNAME": true, + "SYS__LWP_SETPRIVATE": true, + "SYS__LWP_SUSPEND": true, + "SYS__LWP_UNPARK": true, + "SYS__LWP_UNPARK_ALL": true, + "SYS__LWP_WAIT": true, + "SYS__LWP_WAKEUP": true, + "SYS__NEWSELECT": true, + "SYS__PSET_BIND": true, + "SYS__SCHED_GETAFFINITY": true, + "SYS__SCHED_GETPARAM": true, + "SYS__SCHED_SETAFFINITY": true, + "SYS__SCHED_SETPARAM": true, + "SYS__SYSCTL": true, + "SYS__UMTX_LOCK": true, + "SYS__UMTX_OP": true, + "SYS__UMTX_UNLOCK": true, + "SYS___ACL_ACLCHECK_FD": true, + "SYS___ACL_ACLCHECK_FILE": true, + "SYS___ACL_ACLCHECK_LINK": true, + "SYS___ACL_DELETE_FD": true, + "SYS___ACL_DELETE_FILE": true, + "SYS___ACL_DELETE_LINK": true, + "SYS___ACL_GET_FD": true, + "SYS___ACL_GET_FILE": true, + "SYS___ACL_GET_LINK": true, + "SYS___ACL_SET_FD": true, + "SYS___ACL_SET_FILE": true, + "SYS___ACL_SET_LINK": true, + "SYS___CLONE": true, + "SYS___DISABLE_THREADSIGNAL": true, + "SYS___GETCWD": true, + "SYS___GETLOGIN": true, + "SYS___GET_TCB": true, + "SYS___MAC_EXECVE": true, + "SYS___MAC_GETFSSTAT": true, + "SYS___MAC_GET_FD": true, + "SYS___MAC_GET_FILE": true, + "SYS___MAC_GET_LCID": true, + "SYS___MAC_GET_LCTX": true, + "SYS___MAC_GET_LINK": true, + "SYS___MAC_GET_MOUNT": true, + "SYS___MAC_GET_PID": true, + "SYS___MAC_GET_PROC": true, + "SYS___MAC_MOUNT": true, + "SYS___MAC_SET_FD": true, + "SYS___MAC_SET_FILE": true, + "SYS___MAC_SET_LCTX": true, + "SYS___MAC_SET_LINK": true, + "SYS___MAC_SET_PROC": true, + "SYS___MAC_SYSCALL": true, + "SYS___OLD_SEMWAIT_SIGNAL": true, + "SYS___OLD_SEMWAIT_SIGNAL_NOCANCEL": true, + "SYS___POSIX_CHOWN": true, + "SYS___POSIX_FCHOWN": true, + "SYS___POSIX_LCHOWN": true, + "SYS___POSIX_RENAME": true, + "SYS___PTHREAD_CANCELED": true, + "SYS___PTHREAD_CHDIR": true, + "SYS___PTHREAD_FCHDIR": true, + "SYS___PTHREAD_KILL": true, + "SYS___PTHREAD_MARKCANCEL": true, + "SYS___PTHREAD_SIGMASK": true, + "SYS___QUOTACTL": true, + "SYS___SEMCTL": true, + "SYS___SEMWAIT_SIGNAL": true, + "SYS___SEMWAIT_SIGNAL_NOCANCEL": true, + "SYS___SETLOGIN": true, + "SYS___SETUGID": true, + "SYS___SET_TCB": true, + "SYS___SIGACTION_SIGTRAMP": true, + "SYS___SIGTIMEDWAIT": true, + "SYS___SIGWAIT": true, + "SYS___SIGWAIT_NOCANCEL": true, + "SYS___SYSCTL": true, + "SYS___TFORK": true, + "SYS___THREXIT": true, + "SYS___THRSIGDIVERT": true, + "SYS___THRSLEEP": true, + "SYS___THRWAKEUP": true, + "S_ARCH1": true, + "S_ARCH2": true, + "S_BLKSIZE": true, + "S_IEXEC": true, + "S_IFBLK": true, + "S_IFCHR": true, + "S_IFDIR": true, + "S_IFIFO": true, + "S_IFLNK": true, + "S_IFMT": true, + "S_IFREG": true, + "S_IFSOCK": true, + "S_IFWHT": true, + "S_IREAD": true, + "S_IRGRP": true, + "S_IROTH": true, + "S_IRUSR": true, + "S_IRWXG": true, + "S_IRWXO": true, + "S_IRWXU": true, + "S_ISGID": true, + "S_ISTXT": true, + "S_ISUID": true, + "S_ISVTX": true, + "S_IWGRP": true, + "S_IWOTH": true, + "S_IWRITE": true, + "S_IWUSR": true, + "S_IXGRP": true, + "S_IXOTH": true, + "S_IXUSR": true, + "S_LOGIN_SET": true, + "SecurityAttributes": true, + "Seek": true, + "Select": true, + "Sendfile": true, + "Sendmsg": true, + "SendmsgN": true, + "Sendto": true, + "Servent": true, + "SetBpf": true, + "SetBpfBuflen": true, + "SetBpfDatalink": true, + "SetBpfHeadercmpl": true, + "SetBpfImmediate": true, + "SetBpfInterface": true, + "SetBpfPromisc": true, + "SetBpfTimeout": true, + "SetCurrentDirectory": true, + "SetEndOfFile": true, + "SetEnvironmentVariable": true, + "SetFileAttributes": true, + "SetFileCompletionNotificationModes": true, + "SetFilePointer": true, + "SetFileTime": true, + "SetHandleInformation": true, + "SetKevent": true, + "SetLsfPromisc": true, + "SetNonblock": true, + "Setdomainname": true, + "Setegid": true, + "Setenv": true, + "Seteuid": true, + "Setfsgid": true, + "Setfsuid": true, + "Setgid": true, + "Setgroups": true, + "Sethostname": true, + "Setlogin": true, + "Setpgid": true, + "Setpriority": true, + "Setprivexec": true, + "Setregid": true, + "Setresgid": true, + "Setresuid": true, + "Setreuid": true, + "Setrlimit": true, + "Setsid": true, + "Setsockopt": true, + "SetsockoptByte": true, + "SetsockoptICMPv6Filter": true, + "SetsockoptIPMreq": true, + "SetsockoptIPMreqn": true, + "SetsockoptIPv6Mreq": true, + "SetsockoptInet4Addr": true, + "SetsockoptInt": true, + "SetsockoptLinger": true, + "SetsockoptString": true, + "SetsockoptTimeval": true, + "Settimeofday": true, + "Setuid": true, + "Setxattr": true, + "Shutdown": true, + "SidTypeAlias": true, + "SidTypeComputer": true, + "SidTypeDeletedAccount": true, + "SidTypeDomain": true, + "SidTypeGroup": true, + "SidTypeInvalid": true, + "SidTypeLabel": true, + "SidTypeUnknown": true, + "SidTypeUser": true, + "SidTypeWellKnownGroup": true, + "Signal": true, + "SizeofBpfHdr": true, + "SizeofBpfInsn": true, + "SizeofBpfProgram": true, + "SizeofBpfStat": true, + "SizeofBpfVersion": true, + "SizeofBpfZbuf": true, + "SizeofBpfZbufHeader": true, + "SizeofCmsghdr": true, + "SizeofICMPv6Filter": true, + "SizeofIPMreq": true, + "SizeofIPMreqn": true, + "SizeofIPv6MTUInfo": true, + "SizeofIPv6Mreq": true, + "SizeofIfAddrmsg": true, + "SizeofIfAnnounceMsghdr": true, + "SizeofIfData": true, + "SizeofIfInfomsg": true, + "SizeofIfMsghdr": true, + "SizeofIfaMsghdr": true, + "SizeofIfmaMsghdr": true, + "SizeofIfmaMsghdr2": true, + "SizeofInet4Pktinfo": true, + "SizeofInet6Pktinfo": true, + "SizeofInotifyEvent": true, + "SizeofLinger": true, + "SizeofMsghdr": true, + "SizeofNlAttr": true, + "SizeofNlMsgerr": true, + "SizeofNlMsghdr": true, + "SizeofRtAttr": true, + "SizeofRtGenmsg": true, + "SizeofRtMetrics": true, + "SizeofRtMsg": true, + "SizeofRtMsghdr": true, + "SizeofRtNexthop": true, + "SizeofSockFilter": true, + "SizeofSockFprog": true, + "SizeofSockaddrAny": true, + "SizeofSockaddrDatalink": true, + "SizeofSockaddrInet4": true, + "SizeofSockaddrInet6": true, + "SizeofSockaddrLinklayer": true, + "SizeofSockaddrNetlink": true, + "SizeofSockaddrUnix": true, + "SizeofTCPInfo": true, + "SizeofUcred": true, + "SlicePtrFromStrings": true, + "SockFilter": true, + "SockFprog": true, + "SockaddrDatalink": true, + "SockaddrGen": true, + "SockaddrInet4": true, + "SockaddrInet6": true, + "SockaddrLinklayer": true, + "SockaddrNetlink": true, + "SockaddrUnix": true, + "Socket": true, + "SocketControlMessage": true, + "SocketDisableIPv6": true, + "Socketpair": true, + "Splice": true, + "StartProcess": true, + "StartupInfo": true, + "Stat": true, + "Stat_t": true, + "Statfs": true, + "Statfs_t": true, + "Stderr": true, + "Stdin": true, + "Stdout": true, + "StringBytePtr": true, + "StringByteSlice": true, + "StringSlicePtr": true, + "StringToSid": true, + "StringToUTF16": true, + "StringToUTF16Ptr": true, + "Symlink": true, + "Sync": true, + "SyncFileRange": true, + "SysProcAttr": true, + "SysProcIDMap": true, + "Syscall": true, + "Syscall12": true, + "Syscall15": true, + "Syscall18": true, + "Syscall6": true, + "Syscall9": true, + "Sysctl": true, + "SysctlUint32": true, + "Sysctlnode": true, + "Sysinfo": true, + "Sysinfo_t": true, + "Systemtime": true, + "TCGETS": true, + "TCIFLUSH": true, + "TCIOFLUSH": true, + "TCOFLUSH": true, + "TCPInfo": true, + "TCPKeepalive": true, + "TCP_CA_NAME_MAX": true, + "TCP_CONGCTL": true, + "TCP_CONGESTION": true, + "TCP_CONNECTIONTIMEOUT": true, + "TCP_CORK": true, + "TCP_DEFER_ACCEPT": true, + "TCP_INFO": true, + "TCP_KEEPALIVE": true, + "TCP_KEEPCNT": true, + "TCP_KEEPIDLE": true, + "TCP_KEEPINIT": true, + "TCP_KEEPINTVL": true, + "TCP_LINGER2": true, + "TCP_MAXBURST": true, + "TCP_MAXHLEN": true, + "TCP_MAXOLEN": true, + "TCP_MAXSEG": true, + "TCP_MAXWIN": true, + "TCP_MAX_SACK": true, + "TCP_MAX_WINSHIFT": true, + "TCP_MD5SIG": true, + "TCP_MD5SIG_MAXKEYLEN": true, + "TCP_MINMSS": true, + "TCP_MINMSSOVERLOAD": true, + "TCP_MSS": true, + "TCP_NODELAY": true, + "TCP_NOOPT": true, + "TCP_NOPUSH": true, + "TCP_NSTATES": true, + "TCP_QUICKACK": true, + "TCP_RXT_CONNDROPTIME": true, + "TCP_RXT_FINDROP": true, + "TCP_SACK_ENABLE": true, + "TCP_SYNCNT": true, + "TCP_VENDOR": true, + "TCP_WINDOW_CLAMP": true, + "TCSAFLUSH": true, + "TCSETS": true, + "TF_DISCONNECT": true, + "TF_REUSE_SOCKET": true, + "TF_USE_DEFAULT_WORKER": true, + "TF_USE_KERNEL_APC": true, + "TF_USE_SYSTEM_THREAD": true, + "TF_WRITE_BEHIND": true, + "TH32CS_INHERIT": true, + "TH32CS_SNAPALL": true, + "TH32CS_SNAPHEAPLIST": true, + "TH32CS_SNAPMODULE": true, + "TH32CS_SNAPMODULE32": true, + "TH32CS_SNAPPROCESS": true, + "TH32CS_SNAPTHREAD": true, + "TIME_ZONE_ID_DAYLIGHT": true, + "TIME_ZONE_ID_STANDARD": true, + "TIME_ZONE_ID_UNKNOWN": true, + "TIOCCBRK": true, + "TIOCCDTR": true, + "TIOCCONS": true, + "TIOCDCDTIMESTAMP": true, + "TIOCDRAIN": true, + "TIOCDSIMICROCODE": true, + "TIOCEXCL": true, + "TIOCEXT": true, + "TIOCFLAG_CDTRCTS": true, + "TIOCFLAG_CLOCAL": true, + "TIOCFLAG_CRTSCTS": true, + "TIOCFLAG_MDMBUF": true, + "TIOCFLAG_PPS": true, + "TIOCFLAG_SOFTCAR": true, + "TIOCFLUSH": true, + "TIOCGDEV": true, + "TIOCGDRAINWAIT": true, + "TIOCGETA": true, + "TIOCGETD": true, + "TIOCGFLAGS": true, + "TIOCGICOUNT": true, + "TIOCGLCKTRMIOS": true, + "TIOCGLINED": true, + "TIOCGPGRP": true, + "TIOCGPTN": true, + "TIOCGQSIZE": true, + "TIOCGRANTPT": true, + "TIOCGRS485": true, + "TIOCGSERIAL": true, + "TIOCGSID": true, + "TIOCGSIZE": true, + "TIOCGSOFTCAR": true, + "TIOCGTSTAMP": true, + "TIOCGWINSZ": true, + "TIOCINQ": true, + "TIOCIXOFF": true, + "TIOCIXON": true, + "TIOCLINUX": true, + "TIOCMBIC": true, + "TIOCMBIS": true, + "TIOCMGDTRWAIT": true, + "TIOCMGET": true, + "TIOCMIWAIT": true, + "TIOCMODG": true, + "TIOCMODS": true, + "TIOCMSDTRWAIT": true, + "TIOCMSET": true, + "TIOCM_CAR": true, + "TIOCM_CD": true, + "TIOCM_CTS": true, + "TIOCM_DCD": true, + "TIOCM_DSR": true, + "TIOCM_DTR": true, + "TIOCM_LE": true, + "TIOCM_RI": true, + "TIOCM_RNG": true, + "TIOCM_RTS": true, + "TIOCM_SR": true, + "TIOCM_ST": true, + "TIOCNOTTY": true, + "TIOCNXCL": true, + "TIOCOUTQ": true, + "TIOCPKT": true, + "TIOCPKT_DATA": true, + "TIOCPKT_DOSTOP": true, + "TIOCPKT_FLUSHREAD": true, + "TIOCPKT_FLUSHWRITE": true, + "TIOCPKT_IOCTL": true, + "TIOCPKT_NOSTOP": true, + "TIOCPKT_START": true, + "TIOCPKT_STOP": true, + "TIOCPTMASTER": true, + "TIOCPTMGET": true, + "TIOCPTSNAME": true, + "TIOCPTYGNAME": true, + "TIOCPTYGRANT": true, + "TIOCPTYUNLK": true, + "TIOCRCVFRAME": true, + "TIOCREMOTE": true, + "TIOCSBRK": true, + "TIOCSCONS": true, + "TIOCSCTTY": true, + "TIOCSDRAINWAIT": true, + "TIOCSDTR": true, + "TIOCSERCONFIG": true, + "TIOCSERGETLSR": true, + "TIOCSERGETMULTI": true, + "TIOCSERGSTRUCT": true, + "TIOCSERGWILD": true, + "TIOCSERSETMULTI": true, + "TIOCSERSWILD": true, + "TIOCSER_TEMT": true, + "TIOCSETA": true, + "TIOCSETAF": true, + "TIOCSETAW": true, + "TIOCSETD": true, + "TIOCSFLAGS": true, + "TIOCSIG": true, + "TIOCSLCKTRMIOS": true, + "TIOCSLINED": true, + "TIOCSPGRP": true, + "TIOCSPTLCK": true, + "TIOCSQSIZE": true, + "TIOCSRS485": true, + "TIOCSSERIAL": true, + "TIOCSSIZE": true, + "TIOCSSOFTCAR": true, + "TIOCSTART": true, + "TIOCSTAT": true, + "TIOCSTI": true, + "TIOCSTOP": true, + "TIOCSTSTAMP": true, + "TIOCSWINSZ": true, + "TIOCTIMESTAMP": true, + "TIOCUCNTL": true, + "TIOCVHANGUP": true, + "TIOCXMTFRAME": true, + "TOKEN_ADJUST_DEFAULT": true, + "TOKEN_ADJUST_GROUPS": true, + "TOKEN_ADJUST_PRIVILEGES": true, + "TOKEN_ADJUST_SESSIONID": true, + "TOKEN_ALL_ACCESS": true, + "TOKEN_ASSIGN_PRIMARY": true, + "TOKEN_DUPLICATE": true, + "TOKEN_EXECUTE": true, + "TOKEN_IMPERSONATE": true, + "TOKEN_QUERY": true, + "TOKEN_QUERY_SOURCE": true, + "TOKEN_READ": true, + "TOKEN_WRITE": true, + "TOSTOP": true, + "TRUNCATE_EXISTING": true, + "TUNATTACHFILTER": true, + "TUNDETACHFILTER": true, + "TUNGETFEATURES": true, + "TUNGETIFF": true, + "TUNGETSNDBUF": true, + "TUNGETVNETHDRSZ": true, + "TUNSETDEBUG": true, + "TUNSETGROUP": true, + "TUNSETIFF": true, + "TUNSETLINK": true, + "TUNSETNOCSUM": true, + "TUNSETOFFLOAD": true, + "TUNSETOWNER": true, + "TUNSETPERSIST": true, + "TUNSETSNDBUF": true, + "TUNSETTXFILTER": true, + "TUNSETVNETHDRSZ": true, + "Tee": true, + "TerminateProcess": true, + "Termios": true, + "Tgkill": true, + "Time": true, + "Time_t": true, + "Times": true, + "Timespec": true, + "TimespecToNsec": true, + "Timeval": true, + "Timeval32": true, + "TimevalToNsec": true, + "Timex": true, + "Timezoneinformation": true, + "Tms": true, + "Token": true, + "TokenAccessInformation": true, + "TokenAuditPolicy": true, + "TokenDefaultDacl": true, + "TokenElevation": true, + "TokenElevationType": true, + "TokenGroups": true, + "TokenGroupsAndPrivileges": true, + "TokenHasRestrictions": true, + "TokenImpersonationLevel": true, + "TokenIntegrityLevel": true, + "TokenLinkedToken": true, + "TokenLogonSid": true, + "TokenMandatoryPolicy": true, + "TokenOrigin": true, + "TokenOwner": true, + "TokenPrimaryGroup": true, + "TokenPrivileges": true, + "TokenRestrictedSids": true, + "TokenSandBoxInert": true, + "TokenSessionId": true, + "TokenSessionReference": true, + "TokenSource": true, + "TokenStatistics": true, + "TokenType": true, + "TokenUIAccess": true, + "TokenUser": true, + "TokenVirtualizationAllowed": true, + "TokenVirtualizationEnabled": true, + "Tokenprimarygroup": true, + "Tokenuser": true, + "TranslateAccountName": true, + "TranslateName": true, + "TransmitFile": true, + "TransmitFileBuffers": true, + "Truncate": true, + "UNIX_PATH_MAX": true, + "USAGE_MATCH_TYPE_AND": true, + "USAGE_MATCH_TYPE_OR": true, + "UTF16FromString": true, + "UTF16PtrFromString": true, + "UTF16ToString": true, + "Ucred": true, + "Umask": true, + "Uname": true, + "Undelete": true, + "UnixCredentials": true, + "UnixRights": true, + "Unlink": true, + "Unlinkat": true, + "UnmapViewOfFile": true, + "Unmount": true, + "Unsetenv": true, + "Unshare": true, + "UserInfo10": true, + "Ustat": true, + "Ustat_t": true, + "Utimbuf": true, + "Utime": true, + "Utimes": true, + "UtimesNano": true, + "Utsname": true, + "VDISCARD": true, + "VDSUSP": true, + "VEOF": true, + "VEOL": true, + "VEOL2": true, + "VERASE": true, + "VERASE2": true, + "VINTR": true, + "VKILL": true, + "VLNEXT": true, + "VMIN": true, + "VQUIT": true, + "VREPRINT": true, + "VSTART": true, + "VSTATUS": true, + "VSTOP": true, + "VSUSP": true, + "VSWTC": true, + "VT0": true, + "VT1": true, + "VTDLY": true, + "VTIME": true, + "VWERASE": true, + "VirtualLock": true, + "VirtualUnlock": true, + "WAIT_ABANDONED": true, + "WAIT_FAILED": true, + "WAIT_OBJECT_0": true, + "WAIT_TIMEOUT": true, + "WALL": true, + "WALLSIG": true, + "WALTSIG": true, + "WCLONE": true, + "WCONTINUED": true, + "WCOREFLAG": true, + "WEXITED": true, + "WLINUXCLONE": true, + "WNOHANG": true, + "WNOTHREAD": true, + "WNOWAIT": true, + "WNOZOMBIE": true, + "WOPTSCHECKED": true, + "WORDSIZE": true, + "WSABuf": true, + "WSACleanup": true, + "WSADESCRIPTION_LEN": true, + "WSAData": true, + "WSAEACCES": true, + "WSAECONNABORTED": true, + "WSAECONNRESET": true, + "WSAEnumProtocols": true, + "WSAID_CONNECTEX": true, + "WSAIoctl": true, + "WSAPROTOCOL_LEN": true, + "WSAProtocolChain": true, + "WSAProtocolInfo": true, + "WSARecv": true, + "WSARecvFrom": true, + "WSASYS_STATUS_LEN": true, + "WSASend": true, + "WSASendTo": true, + "WSASendto": true, + "WSAStartup": true, + "WSTOPPED": true, + "WTRAPPED": true, + "WUNTRACED": true, + "Wait4": true, + "WaitForSingleObject": true, + "WaitStatus": true, + "Win32FileAttributeData": true, + "Win32finddata": true, + "Write": true, + "WriteConsole": true, + "WriteFile": true, + "X509_ASN_ENCODING": true, + "XCASE": true, + "XP1_CONNECTIONLESS": true, + "XP1_CONNECT_DATA": true, + "XP1_DISCONNECT_DATA": true, + "XP1_EXPEDITED_DATA": true, + "XP1_GRACEFUL_CLOSE": true, + "XP1_GUARANTEED_DELIVERY": true, + "XP1_GUARANTEED_ORDER": true, + "XP1_IFS_HANDLES": true, + "XP1_MESSAGE_ORIENTED": true, + "XP1_MULTIPOINT_CONTROL_PLANE": true, + "XP1_MULTIPOINT_DATA_PLANE": true, + "XP1_PARTIAL_MESSAGE": true, + "XP1_PSEUDO_STREAM": true, + "XP1_QOS_SUPPORTED": true, + "XP1_SAN_SUPPORT_SDP": true, + "XP1_SUPPORT_BROADCAST": true, + "XP1_SUPPORT_MULTIPOINT": true, + "XP1_UNI_RECV": true, + "XP1_UNI_SEND": true, + }, + "syscall/js": map[string]bool{ + "Error": true, + "Func": true, + "FuncOf": true, + "Global": true, + "Null": true, + "Type": true, + "TypeBoolean": true, + "TypeFunction": true, + "TypeNull": true, + "TypeNumber": true, + "TypeObject": true, + "TypeString": true, + "TypeSymbol": true, + "TypeUndefined": true, + "TypedArray": true, + "TypedArrayOf": true, + "Undefined": true, + "Value": true, + "ValueError": true, + "ValueOf": true, + "Wrapper": true, + }, + "testing": map[string]bool{ + "AllocsPerRun": true, + "B": true, + "Benchmark": true, + "BenchmarkResult": true, + "Cover": true, + "CoverBlock": true, + "CoverMode": true, + "Coverage": true, + "InternalBenchmark": true, + "InternalExample": true, + "InternalTest": true, + "M": true, + "Main": true, + "MainStart": true, + "PB": true, + "RegisterCover": true, + "RunBenchmarks": true, + "RunExamples": true, + "RunTests": true, + "Short": true, + "T": true, + "Verbose": true, + }, + "testing/iotest": map[string]bool{ + "DataErrReader": true, + "ErrTimeout": true, + "HalfReader": true, + "NewReadLogger": true, + "NewWriteLogger": true, + "OneByteReader": true, + "TimeoutReader": true, + "TruncateWriter": true, + }, + "testing/quick": map[string]bool{ + "Check": true, + "CheckEqual": true, + "CheckEqualError": true, + "CheckError": true, + "Config": true, + "Generator": true, + "SetupError": true, + "Value": true, + }, + "text/scanner": map[string]bool{ + "Char": true, + "Comment": true, + "EOF": true, + "Float": true, + "GoTokens": true, + "GoWhitespace": true, + "Ident": true, + "Int": true, + "Position": true, + "RawString": true, + "ScanChars": true, + "ScanComments": true, + "ScanFloats": true, + "ScanIdents": true, + "ScanInts": true, + "ScanRawStrings": true, + "ScanStrings": true, + "Scanner": true, + "SkipComments": true, + "String": true, + "TokenString": true, + }, + "text/tabwriter": map[string]bool{ + "AlignRight": true, + "Debug": true, + "DiscardEmptyColumns": true, + "Escape": true, + "FilterHTML": true, + "NewWriter": true, + "StripEscape": true, + "TabIndent": true, + "Writer": true, + }, + "text/template": map[string]bool{ + "ExecError": true, + "FuncMap": true, + "HTMLEscape": true, + "HTMLEscapeString": true, + "HTMLEscaper": true, + "IsTrue": true, + "JSEscape": true, + "JSEscapeString": true, + "JSEscaper": true, + "Must": true, + "New": true, + "ParseFiles": true, + "ParseGlob": true, + "Template": true, + "URLQueryEscaper": true, + }, + "text/template/parse": map[string]bool{ + "ActionNode": true, + "BoolNode": true, + "BranchNode": true, + "ChainNode": true, + "CommandNode": true, + "DotNode": true, + "FieldNode": true, + "IdentifierNode": true, + "IfNode": true, + "IsEmptyTree": true, + "ListNode": true, + "New": true, + "NewIdentifier": true, + "NilNode": true, + "Node": true, + "NodeAction": true, + "NodeBool": true, + "NodeChain": true, + "NodeCommand": true, + "NodeDot": true, + "NodeField": true, + "NodeIdentifier": true, + "NodeIf": true, + "NodeList": true, + "NodeNil": true, + "NodeNumber": true, + "NodePipe": true, + "NodeRange": true, + "NodeString": true, + "NodeTemplate": true, + "NodeText": true, + "NodeType": true, + "NodeVariable": true, + "NodeWith": true, + "NumberNode": true, + "Parse": true, + "PipeNode": true, + "Pos": true, + "RangeNode": true, + "StringNode": true, + "TemplateNode": true, + "TextNode": true, + "Tree": true, + "VariableNode": true, + "WithNode": true, + }, + "time": map[string]bool{ + "ANSIC": true, + "After": true, + "AfterFunc": true, + "April": true, + "August": true, + "Date": true, + "December": true, + "Duration": true, + "February": true, + "FixedZone": true, + "Friday": true, + "Hour": true, + "January": true, + "July": true, + "June": true, + "Kitchen": true, + "LoadLocation": true, + "LoadLocationFromTZData": true, + "Local": true, + "Location": true, + "March": true, + "May": true, + "Microsecond": true, + "Millisecond": true, + "Minute": true, + "Monday": true, + "Month": true, + "Nanosecond": true, + "NewTicker": true, + "NewTimer": true, + "November": true, + "Now": true, + "October": true, + "Parse": true, + "ParseDuration": true, + "ParseError": true, + "ParseInLocation": true, + "RFC1123": true, + "RFC1123Z": true, + "RFC3339": true, + "RFC3339Nano": true, + "RFC822": true, + "RFC822Z": true, + "RFC850": true, + "RubyDate": true, + "Saturday": true, + "Second": true, + "September": true, + "Since": true, + "Sleep": true, + "Stamp": true, + "StampMicro": true, + "StampMilli": true, + "StampNano": true, + "Sunday": true, + "Thursday": true, + "Tick": true, + "Ticker": true, + "Time": true, + "Timer": true, + "Tuesday": true, + "UTC": true, + "Unix": true, + "UnixDate": true, + "Until": true, + "Wednesday": true, + "Weekday": true, + }, + "unicode": map[string]bool{ + "ASCII_Hex_Digit": true, + "Adlam": true, + "Ahom": true, + "Anatolian_Hieroglyphs": true, + "Arabic": true, + "Armenian": true, + "Avestan": true, + "AzeriCase": true, + "Balinese": true, + "Bamum": true, + "Bassa_Vah": true, + "Batak": true, + "Bengali": true, + "Bhaiksuki": true, + "Bidi_Control": true, + "Bopomofo": true, + "Brahmi": true, + "Braille": true, + "Buginese": true, + "Buhid": true, + "C": true, + "Canadian_Aboriginal": true, + "Carian": true, + "CaseRange": true, + "CaseRanges": true, + "Categories": true, + "Caucasian_Albanian": true, + "Cc": true, + "Cf": true, + "Chakma": true, + "Cham": true, + "Cherokee": true, + "Co": true, + "Common": true, + "Coptic": true, + "Cs": true, + "Cuneiform": true, + "Cypriot": true, + "Cyrillic": true, + "Dash": true, + "Deprecated": true, + "Deseret": true, + "Devanagari": true, + "Diacritic": true, + "Digit": true, + "Duployan": true, + "Egyptian_Hieroglyphs": true, + "Elbasan": true, + "Ethiopic": true, + "Extender": true, + "FoldCategory": true, + "FoldScript": true, + "Georgian": true, + "Glagolitic": true, + "Gothic": true, + "Grantha": true, + "GraphicRanges": true, + "Greek": true, + "Gujarati": true, + "Gurmukhi": true, + "Han": true, + "Hangul": true, + "Hanunoo": true, + "Hatran": true, + "Hebrew": true, + "Hex_Digit": true, + "Hiragana": true, + "Hyphen": true, + "IDS_Binary_Operator": true, + "IDS_Trinary_Operator": true, + "Ideographic": true, + "Imperial_Aramaic": true, + "In": true, + "Inherited": true, + "Inscriptional_Pahlavi": true, + "Inscriptional_Parthian": true, + "Is": true, + "IsControl": true, + "IsDigit": true, + "IsGraphic": true, + "IsLetter": true, + "IsLower": true, + "IsMark": true, + "IsNumber": true, + "IsOneOf": true, + "IsPrint": true, + "IsPunct": true, + "IsSpace": true, + "IsSymbol": true, + "IsTitle": true, + "IsUpper": true, + "Javanese": true, + "Join_Control": true, + "Kaithi": true, + "Kannada": true, + "Katakana": true, + "Kayah_Li": true, + "Kharoshthi": true, + "Khmer": true, + "Khojki": true, + "Khudawadi": true, + "L": true, + "Lao": true, + "Latin": true, + "Lepcha": true, + "Letter": true, + "Limbu": true, + "Linear_A": true, + "Linear_B": true, + "Lisu": true, + "Ll": true, + "Lm": true, + "Lo": true, + "Logical_Order_Exception": true, + "Lower": true, + "LowerCase": true, + "Lt": true, + "Lu": true, + "Lycian": true, + "Lydian": true, + "M": true, + "Mahajani": true, + "Malayalam": true, + "Mandaic": true, + "Manichaean": true, + "Marchen": true, + "Mark": true, + "Masaram_Gondi": true, + "MaxASCII": true, + "MaxCase": true, + "MaxLatin1": true, + "MaxRune": true, + "Mc": true, + "Me": true, + "Meetei_Mayek": true, + "Mende_Kikakui": true, + "Meroitic_Cursive": true, + "Meroitic_Hieroglyphs": true, + "Miao": true, + "Mn": true, + "Modi": true, + "Mongolian": true, + "Mro": true, + "Multani": true, + "Myanmar": true, + "N": true, + "Nabataean": true, + "Nd": true, + "New_Tai_Lue": true, + "Newa": true, + "Nko": true, + "Nl": true, + "No": true, + "Noncharacter_Code_Point": true, + "Number": true, + "Nushu": true, + "Ogham": true, + "Ol_Chiki": true, + "Old_Hungarian": true, + "Old_Italic": true, + "Old_North_Arabian": true, + "Old_Permic": true, + "Old_Persian": true, + "Old_South_Arabian": true, + "Old_Turkic": true, + "Oriya": true, + "Osage": true, + "Osmanya": true, + "Other": true, + "Other_Alphabetic": true, + "Other_Default_Ignorable_Code_Point": true, + "Other_Grapheme_Extend": true, + "Other_ID_Continue": true, + "Other_ID_Start": true, + "Other_Lowercase": true, + "Other_Math": true, + "Other_Uppercase": true, + "P": true, + "Pahawh_Hmong": true, + "Palmyrene": true, + "Pattern_Syntax": true, + "Pattern_White_Space": true, + "Pau_Cin_Hau": true, + "Pc": true, + "Pd": true, + "Pe": true, + "Pf": true, + "Phags_Pa": true, + "Phoenician": true, + "Pi": true, + "Po": true, + "Prepended_Concatenation_Mark": true, + "PrintRanges": true, + "Properties": true, + "Ps": true, + "Psalter_Pahlavi": true, + "Punct": true, + "Quotation_Mark": true, + "Radical": true, + "Range16": true, + "Range32": true, + "RangeTable": true, + "Regional_Indicator": true, + "Rejang": true, + "ReplacementChar": true, + "Runic": true, + "S": true, + "STerm": true, + "Samaritan": true, + "Saurashtra": true, + "Sc": true, + "Scripts": true, + "Sentence_Terminal": true, + "Sharada": true, + "Shavian": true, + "Siddham": true, + "SignWriting": true, + "SimpleFold": true, + "Sinhala": true, + "Sk": true, + "Sm": true, + "So": true, + "Soft_Dotted": true, + "Sora_Sompeng": true, + "Soyombo": true, + "Space": true, + "SpecialCase": true, + "Sundanese": true, + "Syloti_Nagri": true, + "Symbol": true, + "Syriac": true, + "Tagalog": true, + "Tagbanwa": true, + "Tai_Le": true, + "Tai_Tham": true, + "Tai_Viet": true, + "Takri": true, + "Tamil": true, + "Tangut": true, + "Telugu": true, + "Terminal_Punctuation": true, + "Thaana": true, + "Thai": true, + "Tibetan": true, + "Tifinagh": true, + "Tirhuta": true, + "Title": true, + "TitleCase": true, + "To": true, + "ToLower": true, + "ToTitle": true, + "ToUpper": true, + "TurkishCase": true, + "Ugaritic": true, + "Unified_Ideograph": true, + "Upper": true, + "UpperCase": true, + "UpperLower": true, + "Vai": true, + "Variation_Selector": true, + "Version": true, + "Warang_Citi": true, + "White_Space": true, + "Yi": true, + "Z": true, + "Zanabazar_Square": true, + "Zl": true, + "Zp": true, + "Zs": true, + }, + "unicode/utf16": map[string]bool{ + "Decode": true, + "DecodeRune": true, + "Encode": true, + "EncodeRune": true, + "IsSurrogate": true, + }, + "unicode/utf8": map[string]bool{ + "DecodeLastRune": true, + "DecodeLastRuneInString": true, + "DecodeRune": true, + "DecodeRuneInString": true, + "EncodeRune": true, + "FullRune": true, + "FullRuneInString": true, + "MaxRune": true, + "RuneCount": true, + "RuneCountInString": true, + "RuneError": true, + "RuneLen": true, + "RuneSelf": true, + "RuneStart": true, + "UTFMax": true, + "Valid": true, + "ValidRune": true, + "ValidString": true, + }, + "unsafe": map[string]bool{ + "Alignof": true, + "ArbitraryType": true, + "Offsetof": true, + "Pointer": true, + "Sizeof": true, + }, +} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk.go new file mode 100644 index 0000000000..7219c8e9ff --- /dev/null +++ b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk.go @@ -0,0 +1,196 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fastwalk provides a faster version of filepath.Walk for file system +// scanning tools. +package fastwalk + +import ( + "errors" + "os" + "path/filepath" + "runtime" + "sync" +) + +// TraverseLink is used as a return value from WalkFuncs to indicate that the +// symlink named in the call may be traversed. +var TraverseLink = errors.New("fastwalk: traverse symlink, assuming target is a directory") + +// SkipFiles is a used as a return value from WalkFuncs to indicate that the +// callback should not be called for any other files in the current directory. +// Child directories will still be traversed. +var SkipFiles = errors.New("fastwalk: skip remaining files in directory") + +// Walk is a faster implementation of filepath.Walk. +// +// filepath.Walk's design necessarily calls os.Lstat on each file, +// even if the caller needs less info. +// Many tools need only the type of each file. +// On some platforms, this information is provided directly by the readdir +// system call, avoiding the need to stat each file individually. +// fastwalk_unix.go contains a fork of the syscall routines. +// +// See golang.org/issue/16399 +// +// Walk walks the file tree rooted at root, calling walkFn for +// each file or directory in the tree, including root. +// +// If fastWalk returns filepath.SkipDir, the directory is skipped. +// +// Unlike filepath.Walk: +// * file stat calls must be done by the user. +// The only provided metadata is the file type, which does not include +// any permission bits. +// * multiple goroutines stat the filesystem concurrently. The provided +// walkFn must be safe for concurrent use. +// * fastWalk can follow symlinks if walkFn returns the TraverseLink +// sentinel error. It is the walkFn's responsibility to prevent +// fastWalk from going into symlink cycles. +func Walk(root string, walkFn func(path string, typ os.FileMode) error) error { + // TODO(bradfitz): make numWorkers configurable? We used a + // minimum of 4 to give the kernel more info about multiple + // things we want, in hopes its I/O scheduling can take + // advantage of that. Hopefully most are in cache. Maybe 4 is + // even too low of a minimum. Profile more. + numWorkers := 4 + if n := runtime.NumCPU(); n > numWorkers { + numWorkers = n + } + + // Make sure to wait for all workers to finish, otherwise + // walkFn could still be called after returning. This Wait call + // runs after close(e.donec) below. + var wg sync.WaitGroup + defer wg.Wait() + + w := &walker{ + fn: walkFn, + enqueuec: make(chan walkItem, numWorkers), // buffered for performance + workc: make(chan walkItem, numWorkers), // buffered for performance + donec: make(chan struct{}), + + // buffered for correctness & not leaking goroutines: + resc: make(chan error, numWorkers), + } + defer close(w.donec) + + for i := 0; i < numWorkers; i++ { + wg.Add(1) + go w.doWork(&wg) + } + todo := []walkItem{{dir: root}} + out := 0 + for { + workc := w.workc + var workItem walkItem + if len(todo) == 0 { + workc = nil + } else { + workItem = todo[len(todo)-1] + } + select { + case workc <- workItem: + todo = todo[:len(todo)-1] + out++ + case it := <-w.enqueuec: + todo = append(todo, it) + case err := <-w.resc: + out-- + if err != nil { + return err + } + if out == 0 && len(todo) == 0 { + // It's safe to quit here, as long as the buffered + // enqueue channel isn't also readable, which might + // happen if the worker sends both another unit of + // work and its result before the other select was + // scheduled and both w.resc and w.enqueuec were + // readable. + select { + case it := <-w.enqueuec: + todo = append(todo, it) + default: + return nil + } + } + } + } +} + +// doWork reads directories as instructed (via workc) and runs the +// user's callback function. +func (w *walker) doWork(wg *sync.WaitGroup) { + defer wg.Done() + for { + select { + case <-w.donec: + return + case it := <-w.workc: + select { + case <-w.donec: + return + case w.resc <- w.walk(it.dir, !it.callbackDone): + } + } + } +} + +type walker struct { + fn func(path string, typ os.FileMode) error + + donec chan struct{} // closed on fastWalk's return + workc chan walkItem // to workers + enqueuec chan walkItem // from workers + resc chan error // from workers +} + +type walkItem struct { + dir string + callbackDone bool // callback already called; don't do it again +} + +func (w *walker) enqueue(it walkItem) { + select { + case w.enqueuec <- it: + case <-w.donec: + } +} + +func (w *walker) onDirEnt(dirName, baseName string, typ os.FileMode) error { + joined := dirName + string(os.PathSeparator) + baseName + if typ == os.ModeDir { + w.enqueue(walkItem{dir: joined}) + return nil + } + + err := w.fn(joined, typ) + if typ == os.ModeSymlink { + if err == TraverseLink { + // Set callbackDone so we don't call it twice for both the + // symlink-as-symlink and the symlink-as-directory later: + w.enqueue(walkItem{dir: joined, callbackDone: true}) + return nil + } + if err == filepath.SkipDir { + // Permit SkipDir on symlinks too. + return nil + } + } + return err +} + +func (w *walker) walk(root string, runUserCallback bool) error { + if runUserCallback { + err := w.fn(root, os.ModeDir) + if err == filepath.SkipDir { + return nil + } + if err != nil { + return err + } + } + + return readDir(root, w.onDirEnt) +} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_fileno.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_fileno.go new file mode 100644 index 0000000000..ccffec5adc --- /dev/null +++ b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_fileno.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd openbsd netbsd + +package fastwalk + +import "syscall" + +func direntInode(dirent *syscall.Dirent) uint64 { + return uint64(dirent.Fileno) +} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go new file mode 100644 index 0000000000..ab7fbc0a9a --- /dev/null +++ b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go @@ -0,0 +1,14 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin +// +build !appengine + +package fastwalk + +import "syscall" + +func direntInode(dirent *syscall.Dirent) uint64 { + return uint64(dirent.Ino) +} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_bsd.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_bsd.go new file mode 100644 index 0000000000..a3b26a7bae --- /dev/null +++ b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_bsd.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd openbsd netbsd + +package fastwalk + +import "syscall" + +func direntNamlen(dirent *syscall.Dirent) uint64 { + return uint64(dirent.Namlen) +} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_linux.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_linux.go new file mode 100644 index 0000000000..e880d358b1 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_linux.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux +// +build !appengine + +package fastwalk + +import ( + "bytes" + "syscall" + "unsafe" +) + +func direntNamlen(dirent *syscall.Dirent) uint64 { + const fixedHdr = uint16(unsafe.Offsetof(syscall.Dirent{}.Name)) + nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) + const nameBufLen = uint16(len(nameBuf)) + limit := dirent.Reclen - fixedHdr + if limit > nameBufLen { + limit = nameBufLen + } + nameLen := bytes.IndexByte(nameBuf[:limit], 0) + if nameLen < 0 { + panic("failed to find terminating 0 byte in dirent") + } + return uint64(nameLen) +} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go new file mode 100644 index 0000000000..a906b87595 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go @@ -0,0 +1,37 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd + +package fastwalk + +import ( + "io/ioutil" + "os" +) + +// readDir calls fn for each directory entry in dirName. +// It does not descend into directories or follow symlinks. +// If fn returns a non-nil error, readDir returns with that error +// immediately. +func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { + fis, err := ioutil.ReadDir(dirName) + if err != nil { + return err + } + skipFiles := false + for _, fi := range fis { + if fi.Mode().IsRegular() && skipFiles { + continue + } + if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil { + if err == SkipFiles { + skipFiles = true + continue + } + return err + } + } + return nil +} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go new file mode 100644 index 0000000000..3369b1a0b2 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go @@ -0,0 +1,127 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin freebsd openbsd netbsd +// +build !appengine + +package fastwalk + +import ( + "fmt" + "os" + "syscall" + "unsafe" +) + +const blockSize = 8 << 10 + +// unknownFileMode is a sentinel (and bogus) os.FileMode +// value used to represent a syscall.DT_UNKNOWN Dirent.Type. +const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice + +func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { + fd, err := syscall.Open(dirName, 0, 0) + if err != nil { + return &os.PathError{Op: "open", Path: dirName, Err: err} + } + defer syscall.Close(fd) + + // The buffer must be at least a block long. + buf := make([]byte, blockSize) // stack-allocated; doesn't escape + bufp := 0 // starting read position in buf + nbuf := 0 // end valid data in buf + skipFiles := false + for { + if bufp >= nbuf { + bufp = 0 + nbuf, err = syscall.ReadDirent(fd, buf) + if err != nil { + return os.NewSyscallError("readdirent", err) + } + if nbuf <= 0 { + return nil + } + } + consumed, name, typ := parseDirEnt(buf[bufp:nbuf]) + bufp += consumed + if name == "" || name == "." || name == ".." { + continue + } + // Fallback for filesystems (like old XFS) that don't + // support Dirent.Type and have DT_UNKNOWN (0) there + // instead. + if typ == unknownFileMode { + fi, err := os.Lstat(dirName + "/" + name) + if err != nil { + // It got deleted in the meantime. + if os.IsNotExist(err) { + continue + } + return err + } + typ = fi.Mode() & os.ModeType + } + if skipFiles && typ.IsRegular() { + continue + } + if err := fn(dirName, name, typ); err != nil { + if err == SkipFiles { + skipFiles = true + continue + } + return err + } + } +} + +func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) { + // golang.org/issue/15653 + dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[0])) + if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v { + panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v)) + } + if len(buf) < int(dirent.Reclen) { + panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen)) + } + consumed = int(dirent.Reclen) + if direntInode(dirent) == 0 { // File absent in directory. + return + } + switch dirent.Type { + case syscall.DT_REG: + typ = 0 + case syscall.DT_DIR: + typ = os.ModeDir + case syscall.DT_LNK: + typ = os.ModeSymlink + case syscall.DT_BLK: + typ = os.ModeDevice + case syscall.DT_FIFO: + typ = os.ModeNamedPipe + case syscall.DT_SOCK: + typ = os.ModeSocket + case syscall.DT_UNKNOWN: + typ = unknownFileMode + default: + // Skip weird things. + // It's probably a DT_WHT (http://lwn.net/Articles/325369/) + // or something. Revisit if/when this package is moved outside + // of goimports. goimports only cares about regular files, + // symlinks, and directories. + return + } + + nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) + nameLen := direntNamlen(dirent) + + // Special cases for common things: + if nameLen == 1 && nameBuf[0] == '.' { + name = "." + } else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' { + name = ".." + } else { + name = string(nameBuf[:nameLen]) + } + return +} diff --git a/vendor/golang.org/x/tools/internal/gopathwalk/walk.go b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go new file mode 100644 index 0000000000..04bb96a362 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go @@ -0,0 +1,250 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gopathwalk is like filepath.Walk but specialized for finding Go +// packages, particularly in $GOPATH and $GOROOT. +package gopathwalk + +import ( + "bufio" + "bytes" + "fmt" + "go/build" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "golang.org/x/tools/internal/fastwalk" +) + +// Options controls the behavior of a Walk call. +type Options struct { + Debug bool // Enable debug logging + ModulesEnabled bool // Search module caches. Also disables legacy goimports ignore rules. +} + +// RootType indicates the type of a Root. +type RootType int + +const ( + RootUnknown RootType = iota + RootGOROOT + RootGOPATH + RootCurrentModule + RootModuleCache + RootOther +) + +// A Root is a starting point for a Walk. +type Root struct { + Path string + Type RootType +} + +// SrcDirsRoots returns the roots from build.Default.SrcDirs(). Not modules-compatible. +func SrcDirsRoots(ctx *build.Context) []Root { + var roots []Root + roots = append(roots, Root{filepath.Join(ctx.GOROOT, "src"), RootGOROOT}) + for _, p := range filepath.SplitList(ctx.GOPATH) { + roots = append(roots, Root{filepath.Join(p, "src"), RootGOPATH}) + } + return roots +} + +// Walk walks Go source directories ($GOROOT, $GOPATH, etc) to find packages. +// For each package found, add will be called (concurrently) with the absolute +// paths of the containing source directory and the package directory. +// add will be called concurrently. +func Walk(roots []Root, add func(root Root, dir string), opts Options) { + for _, root := range roots { + walkDir(root, add, opts) + } +} + +func walkDir(root Root, add func(Root, string), opts Options) { + if _, err := os.Stat(root.Path); os.IsNotExist(err) { + if opts.Debug { + log.Printf("skipping nonexistant directory: %v", root.Path) + } + return + } + if opts.Debug { + log.Printf("scanning %s", root.Path) + } + w := &walker{ + root: root, + add: add, + opts: opts, + } + w.init() + if err := fastwalk.Walk(root.Path, w.walk); err != nil { + log.Printf("gopathwalk: scanning directory %v: %v", root.Path, err) + } + + if opts.Debug { + log.Printf("scanned %s", root.Path) + } +} + +// walker is the callback for fastwalk.Walk. +type walker struct { + root Root // The source directory to scan. + add func(Root, string) // The callback that will be invoked for every possible Go package dir. + opts Options // Options passed to Walk by the user. + + ignoredDirs []os.FileInfo // The ignored directories, loaded from .goimportsignore files. +} + +// init initializes the walker based on its Options. +func (w *walker) init() { + var ignoredPaths []string + if w.root.Type == RootModuleCache { + ignoredPaths = []string{"cache"} + } + if !w.opts.ModulesEnabled && w.root.Type == RootGOPATH { + ignoredPaths = w.getIgnoredDirs(w.root.Path) + ignoredPaths = append(ignoredPaths, "v", "mod") + } + + for _, p := range ignoredPaths { + full := filepath.Join(w.root.Path, p) + if fi, err := os.Stat(full); err == nil { + w.ignoredDirs = append(w.ignoredDirs, fi) + if w.opts.Debug { + log.Printf("Directory added to ignore list: %s", full) + } + } else if w.opts.Debug { + log.Printf("Error statting ignored directory: %v", err) + } + } +} + +// getIgnoredDirs reads an optional config file at /.goimportsignore +// of relative directories to ignore when scanning for go files. +// The provided path is one of the $GOPATH entries with "src" appended. +func (w *walker) getIgnoredDirs(path string) []string { + file := filepath.Join(path, ".goimportsignore") + slurp, err := ioutil.ReadFile(file) + if w.opts.Debug { + if err != nil { + log.Print(err) + } else { + log.Printf("Read %s", file) + } + } + if err != nil { + return nil + } + + var ignoredDirs []string + bs := bufio.NewScanner(bytes.NewReader(slurp)) + for bs.Scan() { + line := strings.TrimSpace(bs.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + ignoredDirs = append(ignoredDirs, line) + } + return ignoredDirs +} + +func (w *walker) shouldSkipDir(fi os.FileInfo) bool { + for _, ignoredDir := range w.ignoredDirs { + if os.SameFile(fi, ignoredDir) { + return true + } + } + return false +} + +func (w *walker) walk(path string, typ os.FileMode) error { + dir := filepath.Dir(path) + if typ.IsRegular() { + if dir == w.root.Path && (w.root.Type == RootGOROOT || w.root.Type == RootGOPATH) { + // Doesn't make sense to have regular files + // directly in your $GOPATH/src or $GOROOT/src. + return fastwalk.SkipFiles + } + if !strings.HasSuffix(path, ".go") { + return nil + } + + w.add(w.root, dir) + return fastwalk.SkipFiles + } + if typ == os.ModeDir { + base := filepath.Base(path) + if base == "" || base[0] == '.' || base[0] == '_' || + base == "testdata" || + (w.root.Type == RootGOROOT && w.opts.ModulesEnabled && base == "vendor") || + (!w.opts.ModulesEnabled && base == "node_modules") { + return filepath.SkipDir + } + fi, err := os.Lstat(path) + if err == nil && w.shouldSkipDir(fi) { + return filepath.SkipDir + } + return nil + } + if typ == os.ModeSymlink { + base := filepath.Base(path) + if strings.HasPrefix(base, ".#") { + // Emacs noise. + return nil + } + fi, err := os.Lstat(path) + if err != nil { + // Just ignore it. + return nil + } + if w.shouldTraverse(dir, fi) { + return fastwalk.TraverseLink + } + } + return nil +} + +// shouldTraverse reports whether the symlink fi, found in dir, +// should be followed. It makes sure symlinks were never visited +// before to avoid symlink loops. +func (w *walker) shouldTraverse(dir string, fi os.FileInfo) bool { + path := filepath.Join(dir, fi.Name()) + target, err := filepath.EvalSymlinks(path) + if err != nil { + return false + } + ts, err := os.Stat(target) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return false + } + if !ts.IsDir() { + return false + } + if w.shouldSkipDir(ts) { + return false + } + // Check for symlink loops by statting each directory component + // and seeing if any are the same file as ts. + for { + parent := filepath.Dir(path) + if parent == path { + // Made it to the root without seeing a cycle. + // Use this symlink. + return true + } + parentInfo, err := os.Stat(parent) + if err != nil { + return false + } + if os.SameFile(ts, parentInfo) { + // Cycle. Don't traverse. + return false + } + path = parent + } + +} diff --git a/vendor/golang.org/x/tools/internal/module/module.go b/vendor/golang.org/x/tools/internal/module/module.go new file mode 100644 index 0000000000..9a4edb9dec --- /dev/null +++ b/vendor/golang.org/x/tools/internal/module/module.go @@ -0,0 +1,540 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package module defines the module.Version type +// along with support code. +package module + +// IMPORTANT NOTE +// +// This file essentially defines the set of valid import paths for the go command. +// There are many subtle considerations, including Unicode ambiguity, +// security, network, and file system representations. +// +// This file also defines the set of valid module path and version combinations, +// another topic with many subtle considerations. +// +// Changes to the semantics in this file require approval from rsc. + +import ( + "fmt" + "sort" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/tools/internal/semver" +) + +// A Version is defined by a module path and version pair. +type Version struct { + Path string + + // Version is usually a semantic version in canonical form. + // There are two exceptions to this general rule. + // First, the top-level target of a build has no specific version + // and uses Version = "". + // Second, during MVS calculations the version "none" is used + // to represent the decision to take no version of a given module. + Version string `json:",omitempty"` +} + +// Check checks that a given module path, version pair is valid. +// In addition to the path being a valid module path +// and the version being a valid semantic version, +// the two must correspond. +// For example, the path "yaml/v2" only corresponds to +// semantic versions beginning with "v2.". +func Check(path, version string) error { + if err := CheckPath(path); err != nil { + return err + } + if !semver.IsValid(version) { + return fmt.Errorf("malformed semantic version %v", version) + } + _, pathMajor, _ := SplitPathVersion(path) + if !MatchPathMajor(version, pathMajor) { + if pathMajor == "" { + pathMajor = "v0 or v1" + } + if pathMajor[0] == '.' { // .v1 + pathMajor = pathMajor[1:] + } + return fmt.Errorf("mismatched module path %v and version %v (want %v)", path, version, pathMajor) + } + return nil +} + +// firstPathOK reports whether r can appear in the first element of a module path. +// The first element of the path must be an LDH domain name, at least for now. +// To avoid case ambiguity, the domain name must be entirely lower case. +func firstPathOK(r rune) bool { + return r == '-' || r == '.' || + '0' <= r && r <= '9' || + 'a' <= r && r <= 'z' +} + +// pathOK reports whether r can appear in an import path element. +// Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: + - . _ and ~. +// This matches what "go get" has historically recognized in import paths. +// TODO(rsc): We would like to allow Unicode letters, but that requires additional +// care in the safe encoding (see note below). +func pathOK(r rune) bool { + if r < utf8.RuneSelf { + return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' || + '0' <= r && r <= '9' || + 'A' <= r && r <= 'Z' || + 'a' <= r && r <= 'z' + } + return false +} + +// fileNameOK reports whether r can appear in a file name. +// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters. +// If we expand the set of allowed characters here, we have to +// work harder at detecting potential case-folding and normalization collisions. +// See note about "safe encoding" below. +func fileNameOK(r rune) bool { + if r < utf8.RuneSelf { + // Entire set of ASCII punctuation, from which we remove characters: + // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ + // We disallow some shell special characters: " ' * < > ? ` | + // (Note that some of those are disallowed by the Windows file system as well.) + // We also disallow path separators / : and \ (fileNameOK is only called on path element characters). + // We allow spaces (U+0020) in file names. + const allowed = "!#$%&()+,-.=@[]^_{}~ " + if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' { + return true + } + for i := 0; i < len(allowed); i++ { + if rune(allowed[i]) == r { + return true + } + } + return false + } + // It may be OK to add more ASCII punctuation here, but only carefully. + // For example Windows disallows < > \, and macOS disallows :, so we must not allow those. + return unicode.IsLetter(r) +} + +// CheckPath checks that a module path is valid. +func CheckPath(path string) error { + if err := checkPath(path, false); err != nil { + return fmt.Errorf("malformed module path %q: %v", path, err) + } + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + if i == 0 { + return fmt.Errorf("malformed module path %q: leading slash", path) + } + if !strings.Contains(path[:i], ".") { + return fmt.Errorf("malformed module path %q: missing dot in first path element", path) + } + if path[0] == '-' { + return fmt.Errorf("malformed module path %q: leading dash in first path element", path) + } + for _, r := range path[:i] { + if !firstPathOK(r) { + return fmt.Errorf("malformed module path %q: invalid char %q in first path element", path, r) + } + } + if _, _, ok := SplitPathVersion(path); !ok { + return fmt.Errorf("malformed module path %q: invalid version", path) + } + return nil +} + +// CheckImportPath checks that an import path is valid. +func CheckImportPath(path string) error { + if err := checkPath(path, false); err != nil { + return fmt.Errorf("malformed import path %q: %v", path, err) + } + return nil +} + +// checkPath checks that a general path is valid. +// It returns an error describing why but not mentioning path. +// Because these checks apply to both module paths and import paths, +// the caller is expected to add the "malformed ___ path %q: " prefix. +// fileName indicates whether the final element of the path is a file name +// (as opposed to a directory name). +func checkPath(path string, fileName bool) error { + if !utf8.ValidString(path) { + return fmt.Errorf("invalid UTF-8") + } + if path == "" { + return fmt.Errorf("empty string") + } + if strings.Contains(path, "..") { + return fmt.Errorf("double dot") + } + if strings.Contains(path, "//") { + return fmt.Errorf("double slash") + } + if path[len(path)-1] == '/' { + return fmt.Errorf("trailing slash") + } + elemStart := 0 + for i, r := range path { + if r == '/' { + if err := checkElem(path[elemStart:i], fileName); err != nil { + return err + } + elemStart = i + 1 + } + } + if err := checkElem(path[elemStart:], fileName); err != nil { + return err + } + return nil +} + +// checkElem checks whether an individual path element is valid. +// fileName indicates whether the element is a file name (not a directory name). +func checkElem(elem string, fileName bool) error { + if elem == "" { + return fmt.Errorf("empty path element") + } + if strings.Count(elem, ".") == len(elem) { + return fmt.Errorf("invalid path element %q", elem) + } + if elem[0] == '.' && !fileName { + return fmt.Errorf("leading dot in path element") + } + if elem[len(elem)-1] == '.' { + return fmt.Errorf("trailing dot in path element") + } + charOK := pathOK + if fileName { + charOK = fileNameOK + } + for _, r := range elem { + if !charOK(r) { + return fmt.Errorf("invalid char %q", r) + } + } + + // Windows disallows a bunch of path elements, sadly. + // See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file + short := elem + if i := strings.Index(short, "."); i >= 0 { + short = short[:i] + } + for _, bad := range badWindowsNames { + if strings.EqualFold(bad, short) { + return fmt.Errorf("disallowed path element %q", elem) + } + } + return nil +} + +// CheckFilePath checks whether a slash-separated file path is valid. +func CheckFilePath(path string) error { + if err := checkPath(path, true); err != nil { + return fmt.Errorf("malformed file path %q: %v", path, err) + } + return nil +} + +// badWindowsNames are the reserved file path elements on Windows. +// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file +var badWindowsNames = []string{ + "CON", + "PRN", + "AUX", + "NUL", + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9", + "LPT1", + "LPT2", + "LPT3", + "LPT4", + "LPT5", + "LPT6", + "LPT7", + "LPT8", + "LPT9", +} + +// SplitPathVersion returns prefix and major version such that prefix+pathMajor == path +// and version is either empty or "/vN" for N >= 2. +// As a special case, gopkg.in paths are recognized directly; +// they require ".vN" instead of "/vN", and for all N, not just N >= 2. +func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) { + if strings.HasPrefix(path, "gopkg.in/") { + return splitGopkgIn(path) + } + + i := len(path) + dot := false + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') { + if path[i-1] == '.' { + dot = true + } + i-- + } + if i <= 1 || i == len(path) || path[i-1] != 'v' || path[i-2] != '/' { + return path, "", true + } + prefix, pathMajor = path[:i-2], path[i-2:] + if dot || len(pathMajor) <= 2 || pathMajor[2] == '0' || pathMajor == "/v1" { + return path, "", false + } + return prefix, pathMajor, true +} + +// splitGopkgIn is like SplitPathVersion but only for gopkg.in paths. +func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) { + if !strings.HasPrefix(path, "gopkg.in/") { + return path, "", false + } + i := len(path) + if strings.HasSuffix(path, "-unstable") { + i -= len("-unstable") + } + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9') { + i-- + } + if i <= 1 || path[i-1] != 'v' || path[i-2] != '.' { + // All gopkg.in paths must end in vN for some N. + return path, "", false + } + prefix, pathMajor = path[:i-2], path[i-2:] + if len(pathMajor) <= 2 || pathMajor[2] == '0' && pathMajor != ".v0" { + return path, "", false + } + return prefix, pathMajor, true +} + +// MatchPathMajor reports whether the semantic version v +// matches the path major version pathMajor. +func MatchPathMajor(v, pathMajor string) bool { + if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") { + pathMajor = strings.TrimSuffix(pathMajor, "-unstable") + } + if strings.HasPrefix(v, "v0.0.0-") && pathMajor == ".v1" { + // Allow old bug in pseudo-versions that generated v0.0.0- pseudoversion for gopkg .v1. + // For example, gopkg.in/yaml.v2@v2.2.1's go.mod requires gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405. + return true + } + m := semver.Major(v) + if pathMajor == "" { + return m == "v0" || m == "v1" || semver.Build(v) == "+incompatible" + } + return (pathMajor[0] == '/' || pathMajor[0] == '.') && m == pathMajor[1:] +} + +// CanonicalVersion returns the canonical form of the version string v. +// It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible". +func CanonicalVersion(v string) string { + cv := semver.Canonical(v) + if semver.Build(v) == "+incompatible" { + cv += "+incompatible" + } + return cv +} + +// Sort sorts the list by Path, breaking ties by comparing Versions. +func Sort(list []Version) { + sort.Slice(list, func(i, j int) bool { + mi := list[i] + mj := list[j] + if mi.Path != mj.Path { + return mi.Path < mj.Path + } + // To help go.sum formatting, allow version/file. + // Compare semver prefix by semver rules, + // file by string order. + vi := mi.Version + vj := mj.Version + var fi, fj string + if k := strings.Index(vi, "/"); k >= 0 { + vi, fi = vi[:k], vi[k:] + } + if k := strings.Index(vj, "/"); k >= 0 { + vj, fj = vj[:k], vj[k:] + } + if vi != vj { + return semver.Compare(vi, vj) < 0 + } + return fi < fj + }) +} + +// Safe encodings +// +// Module paths appear as substrings of file system paths +// (in the download cache) and of web server URLs in the proxy protocol. +// In general we cannot rely on file systems to be case-sensitive, +// nor can we rely on web servers, since they read from file systems. +// That is, we cannot rely on the file system to keep rsc.io/QUOTE +// and rsc.io/quote separate. Windows and macOS don't. +// Instead, we must never require two different casings of a file path. +// Because we want the download cache to match the proxy protocol, +// and because we want the proxy protocol to be possible to serve +// from a tree of static files (which might be stored on a case-insensitive +// file system), the proxy protocol must never require two different casings +// of a URL path either. +// +// One possibility would be to make the safe encoding be the lowercase +// hexadecimal encoding of the actual path bytes. This would avoid ever +// needing different casings of a file path, but it would be fairly illegible +// to most programmers when those paths appeared in the file system +// (including in file paths in compiler errors and stack traces) +// in web server logs, and so on. Instead, we want a safe encoding that +// leaves most paths unaltered. +// +// The safe encoding is this: +// replace every uppercase letter with an exclamation mark +// followed by the letter's lowercase equivalent. +// +// For example, +// github.com/Azure/azure-sdk-for-go -> github.com/!azure/azure-sdk-for-go. +// github.com/GoogleCloudPlatform/cloudsql-proxy -> github.com/!google!cloud!platform/cloudsql-proxy +// github.com/Sirupsen/logrus -> github.com/!sirupsen/logrus. +// +// Import paths that avoid upper-case letters are left unchanged. +// Note that because import paths are ASCII-only and avoid various +// problematic punctuation (like : < and >), the safe encoding is also ASCII-only +// and avoids the same problematic punctuation. +// +// Import paths have never allowed exclamation marks, so there is no +// need to define how to encode a literal !. +// +// Although paths are disallowed from using Unicode (see pathOK above), +// the eventual plan is to allow Unicode letters as well, to assume that +// file systems and URLs are Unicode-safe (storing UTF-8), and apply +// the !-for-uppercase convention. Note however that not all runes that +// are different but case-fold equivalent are an upper/lower pair. +// For example, U+004B ('K'), U+006B ('k'), and U+212A ('K' for Kelvin) +// are considered to case-fold to each other. When we do add Unicode +// letters, we must not assume that upper/lower are the only case-equivalent pairs. +// Perhaps the Kelvin symbol would be disallowed entirely, for example. +// Or perhaps it would encode as "!!k", or perhaps as "(212A)". +// +// Also, it would be nice to allow Unicode marks as well as letters, +// but marks include combining marks, and then we must deal not +// only with case folding but also normalization: both U+00E9 ('é') +// and U+0065 U+0301 ('e' followed by combining acute accent) +// look the same on the page and are treated by some file systems +// as the same path. If we do allow Unicode marks in paths, there +// must be some kind of normalization to allow only one canonical +// encoding of any character used in an import path. + +// EncodePath returns the safe encoding of the given module path. +// It fails if the module path is invalid. +func EncodePath(path string) (encoding string, err error) { + if err := CheckPath(path); err != nil { + return "", err + } + + return encodeString(path) +} + +// EncodeVersion returns the safe encoding of the given module version. +// Versions are allowed to be in non-semver form but must be valid file names +// and not contain exclamation marks. +func EncodeVersion(v string) (encoding string, err error) { + if err := checkElem(v, true); err != nil || strings.Contains(v, "!") { + return "", fmt.Errorf("disallowed version string %q", v) + } + return encodeString(v) +} + +func encodeString(s string) (encoding string, err error) { + haveUpper := false + for _, r := range s { + if r == '!' || r >= utf8.RuneSelf { + // This should be disallowed by CheckPath, but diagnose anyway. + // The correctness of the encoding loop below depends on it. + return "", fmt.Errorf("internal error: inconsistency in EncodePath") + } + if 'A' <= r && r <= 'Z' { + haveUpper = true + } + } + + if !haveUpper { + return s, nil + } + + var buf []byte + for _, r := range s { + if 'A' <= r && r <= 'Z' { + buf = append(buf, '!', byte(r+'a'-'A')) + } else { + buf = append(buf, byte(r)) + } + } + return string(buf), nil +} + +// DecodePath returns the module path of the given safe encoding. +// It fails if the encoding is invalid or encodes an invalid path. +func DecodePath(encoding string) (path string, err error) { + path, ok := decodeString(encoding) + if !ok { + return "", fmt.Errorf("invalid module path encoding %q", encoding) + } + if err := CheckPath(path); err != nil { + return "", fmt.Errorf("invalid module path encoding %q: %v", encoding, err) + } + return path, nil +} + +// DecodeVersion returns the version string for the given safe encoding. +// It fails if the encoding is invalid or encodes an invalid version. +// Versions are allowed to be in non-semver form but must be valid file names +// and not contain exclamation marks. +func DecodeVersion(encoding string) (v string, err error) { + v, ok := decodeString(encoding) + if !ok { + return "", fmt.Errorf("invalid version encoding %q", encoding) + } + if err := checkElem(v, true); err != nil { + return "", fmt.Errorf("disallowed version string %q", v) + } + return v, nil +} + +func decodeString(encoding string) (string, bool) { + var buf []byte + + bang := false + for _, r := range encoding { + if r >= utf8.RuneSelf { + return "", false + } + if bang { + bang = false + if r < 'a' || 'z' < r { + return "", false + } + buf = append(buf, byte(r+'A'-'a')) + continue + } + if r == '!' { + bang = true + continue + } + if 'A' <= r && r <= 'Z' { + return "", false + } + buf = append(buf, byte(r)) + } + if bang { + return "", false + } + return string(buf), true +} diff --git a/vendor/golang.org/x/tools/internal/semver/semver.go b/vendor/golang.org/x/tools/internal/semver/semver.go new file mode 100644 index 0000000000..4af7118e55 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/semver/semver.go @@ -0,0 +1,388 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package semver implements comparison of semantic version strings. +// In this package, semantic version strings must begin with a leading "v", +// as in "v1.0.0". +// +// The general form of a semantic version string accepted by this package is +// +// vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]] +// +// where square brackets indicate optional parts of the syntax; +// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros; +// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers +// using only alphanumeric characters and hyphens; and +// all-numeric PRERELEASE identifiers must not have leading zeros. +// +// This package follows Semantic Versioning 2.0.0 (see semver.org) +// with two exceptions. First, it requires the "v" prefix. Second, it recognizes +// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes) +// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. +package semver + +// parsed returns the parsed form of a semantic version string. +type parsed struct { + major string + minor string + patch string + short string + prerelease string + build string + err string +} + +// IsValid reports whether v is a valid semantic version string. +func IsValid(v string) bool { + _, ok := parse(v) + return ok +} + +// Canonical returns the canonical formatting of the semantic version v. +// It fills in any missing .MINOR or .PATCH and discards build metadata. +// Two semantic versions compare equal only if their canonical formattings +// are identical strings. +// The canonical invalid semantic version is the empty string. +func Canonical(v string) string { + p, ok := parse(v) + if !ok { + return "" + } + if p.build != "" { + return v[:len(v)-len(p.build)] + } + if p.short != "" { + return v + p.short + } + return v +} + +// Major returns the major version prefix of the semantic version v. +// For example, Major("v2.1.0") == "v2". +// If v is an invalid semantic version string, Major returns the empty string. +func Major(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return v[:1+len(pv.major)] +} + +// MajorMinor returns the major.minor version prefix of the semantic version v. +// For example, MajorMinor("v2.1.0") == "v2.1". +// If v is an invalid semantic version string, MajorMinor returns the empty string. +func MajorMinor(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + i := 1 + len(pv.major) + if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor { + return v[:j] + } + return v[:i] + "." + pv.minor +} + +// Prerelease returns the prerelease suffix of the semantic version v. +// For example, Prerelease("v2.1.0-pre+meta") == "-pre". +// If v is an invalid semantic version string, Prerelease returns the empty string. +func Prerelease(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return pv.prerelease +} + +// Build returns the build suffix of the semantic version v. +// For example, Build("v2.1.0+meta") == "+meta". +// If v is an invalid semantic version string, Build returns the empty string. +func Build(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return pv.build +} + +// Compare returns an integer comparing two versions according to +// according to semantic version precedence. +// The result will be 0 if v == w, -1 if v < w, or +1 if v > w. +// +// An invalid semantic version string is considered less than a valid one. +// All invalid semantic version strings compare equal to each other. +func Compare(v, w string) int { + pv, ok1 := parse(v) + pw, ok2 := parse(w) + if !ok1 && !ok2 { + return 0 + } + if !ok1 { + return -1 + } + if !ok2 { + return +1 + } + if c := compareInt(pv.major, pw.major); c != 0 { + return c + } + if c := compareInt(pv.minor, pw.minor); c != 0 { + return c + } + if c := compareInt(pv.patch, pw.patch); c != 0 { + return c + } + return comparePrerelease(pv.prerelease, pw.prerelease) +} + +// Max canonicalizes its arguments and then returns the version string +// that compares greater. +func Max(v, w string) string { + v = Canonical(v) + w = Canonical(w) + if Compare(v, w) > 0 { + return v + } + return w +} + +func parse(v string) (p parsed, ok bool) { + if v == "" || v[0] != 'v' { + p.err = "missing v prefix" + return + } + p.major, v, ok = parseInt(v[1:]) + if !ok { + p.err = "bad major version" + return + } + if v == "" { + p.minor = "0" + p.patch = "0" + p.short = ".0.0" + return + } + if v[0] != '.' { + p.err = "bad minor prefix" + ok = false + return + } + p.minor, v, ok = parseInt(v[1:]) + if !ok { + p.err = "bad minor version" + return + } + if v == "" { + p.patch = "0" + p.short = ".0" + return + } + if v[0] != '.' { + p.err = "bad patch prefix" + ok = false + return + } + p.patch, v, ok = parseInt(v[1:]) + if !ok { + p.err = "bad patch version" + return + } + if len(v) > 0 && v[0] == '-' { + p.prerelease, v, ok = parsePrerelease(v) + if !ok { + p.err = "bad prerelease" + return + } + } + if len(v) > 0 && v[0] == '+' { + p.build, v, ok = parseBuild(v) + if !ok { + p.err = "bad build" + return + } + } + if v != "" { + p.err = "junk on end" + ok = false + return + } + ok = true + return +} + +func parseInt(v string) (t, rest string, ok bool) { + if v == "" { + return + } + if v[0] < '0' || '9' < v[0] { + return + } + i := 1 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + if v[0] == '0' && i != 1 { + return + } + return v[:i], v[i:], true +} + +func parsePrerelease(v string) (t, rest string, ok bool) { + // "A pre-release version MAY be denoted by appending a hyphen and + // a series of dot separated identifiers immediately following the patch version. + // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. + // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes." + if v == "" || v[0] != '-' { + return + } + i := 1 + start := 1 + for i < len(v) && v[i] != '+' { + if !isIdentChar(v[i]) && v[i] != '.' { + return + } + if v[i] == '.' { + if start == i || isBadNum(v[start:i]) { + return + } + start = i + 1 + } + i++ + } + if start == i || isBadNum(v[start:i]) { + return + } + return v[:i], v[i:], true +} + +func parseBuild(v string) (t, rest string, ok bool) { + if v == "" || v[0] != '+' { + return + } + i := 1 + start := 1 + for i < len(v) { + if !isIdentChar(v[i]) { + return + } + if v[i] == '.' { + if start == i { + return + } + start = i + 1 + } + i++ + } + if start == i { + return + } + return v[:i], v[i:], true +} + +func isIdentChar(c byte) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' +} + +func isBadNum(v string) bool { + i := 0 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + return i == len(v) && i > 1 && v[0] == '0' +} + +func isNum(v string) bool { + i := 0 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + return i == len(v) +} + +func compareInt(x, y string) int { + if x == y { + return 0 + } + if len(x) < len(y) { + return -1 + } + if len(x) > len(y) { + return +1 + } + if x < y { + return -1 + } else { + return +1 + } +} + +func comparePrerelease(x, y string) int { + // "When major, minor, and patch are equal, a pre-release version has + // lower precedence than a normal version. + // Example: 1.0.0-alpha < 1.0.0. + // Precedence for two pre-release versions with the same major, minor, + // and patch version MUST be determined by comparing each dot separated + // identifier from left to right until a difference is found as follows: + // identifiers consisting of only digits are compared numerically and + // identifiers with letters or hyphens are compared lexically in ASCII + // sort order. Numeric identifiers always have lower precedence than + // non-numeric identifiers. A larger set of pre-release fields has a + // higher precedence than a smaller set, if all of the preceding + // identifiers are equal. + // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < + // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." + if x == y { + return 0 + } + if x == "" { + return +1 + } + if y == "" { + return -1 + } + for x != "" && y != "" { + x = x[1:] // skip - or . + y = y[1:] // skip - or . + var dx, dy string + dx, x = nextIdent(x) + dy, y = nextIdent(y) + if dx != dy { + ix := isNum(dx) + iy := isNum(dy) + if ix != iy { + if ix { + return -1 + } else { + return +1 + } + } + if ix { + if len(dx) < len(dy) { + return -1 + } + if len(dx) > len(dy) { + return +1 + } + } + if dx < dy { + return -1 + } else { + return +1 + } + } + } + if x == "" { + return -1 + } else { + return +1 + } +} + +func nextIdent(x string) (dx, rest string) { + i := 0 + for i < len(x) && x[i] != '.' { + i++ + } + return x[:i], x[i:] +} diff --git a/vendor/k8s.io/code-generator/LICENSE b/vendor/k8s.io/code-generator/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/k8s.io/code-generator/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/OWNERS b/vendor/k8s.io/code-generator/cmd/client-gen/OWNERS new file mode 100644 index 0000000000..0c408a1aa9 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/OWNERS @@ -0,0 +1,8 @@ +approvers: +- lavalamp +- wojtek-t +- caesarxuchao +reviewers: +- lavalamp +- wojtek-t +- caesarxuchao diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/README.md b/vendor/k8s.io/code-generator/cmd/client-gen/README.md new file mode 100644 index 0000000000..d1d67abdf9 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/README.md @@ -0,0 +1,4 @@ +See [generating-clientset.md](https://git.k8s.io/community/contributors/devel/generating-clientset.md) + + +[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/staging/src/k8s.io/code-generator/client-gen/README.md?pixel)]() diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/args/args.go b/vendor/k8s.io/code-generator/cmd/client-gen/args/args.go new file mode 100644 index 0000000000..f45be1bb83 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/args/args.go @@ -0,0 +1,120 @@ +/* +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 args + +import ( + "fmt" + "path" + + "github.com/spf13/pflag" + "k8s.io/gengo/args" + + "k8s.io/code-generator/cmd/client-gen/types" + codegenutil "k8s.io/code-generator/pkg/util" +) + +var DefaultInputDirs = []string{} + +// ClientGenArgs is a wrapper for arguments to client-gen. +type CustomArgs struct { + // A sorted list of group versions to generate. For each of them the package path is found + // in GroupVersionToInputPath. + Groups []types.GroupVersions + + // Overrides for which types should be included in the client. + IncludedTypesOverrides map[types.GroupVersion][]string + + // ClientsetName is the name of the clientset to be generated. It's + // populated from command-line arguments. + ClientsetName string + // ClientsetAPIPath is the default API HTTP path for generated clients. + ClientsetAPIPath string + // ClientsetOnly determines if we should generate the clients for groups and + // types along with the clientset. It's populated from command-line + // arguments. + ClientsetOnly bool + // FakeClient determines if client-gen generates the fake clients. + FakeClient bool +} + +func NewDefaults() (*args.GeneratorArgs, *CustomArgs) { + genericArgs := args.Default().WithoutDefaultFlagParsing() + customArgs := &CustomArgs{ + ClientsetName: "internalclientset", + ClientsetAPIPath: "/apis", + ClientsetOnly: false, + FakeClient: true, + } + genericArgs.CustomArgs = customArgs + genericArgs.InputDirs = DefaultInputDirs + + if pkg := codegenutil.CurrentPackage(); len(pkg) != 0 { + genericArgs.OutputPackagePath = path.Join(pkg, "pkg/client/clientset") + } + + return genericArgs, customArgs +} + +func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet, inputBase string) { + gvsBuilder := NewGroupVersionsBuilder(&ca.Groups) + pflag.Var(NewGVPackagesValue(gvsBuilder, nil), "input", "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\".") + pflag.Var(NewGVTypesValue(&ca.IncludedTypesOverrides, []string{}), "included-types-overrides", "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient will be used for other group versions.") + pflag.Var(NewInputBasePathValue(gvsBuilder, inputBase), "input-base", "base path to look for the api group.") + pflag.StringVarP(&ca.ClientsetName, "clientset-name", "n", ca.ClientsetName, "the name of the generated clientset package.") + pflag.StringVarP(&ca.ClientsetAPIPath, "clientset-api-path", "", ca.ClientsetAPIPath, "the value of default API HTTP path, starting with / and without trailing /.") + pflag.BoolVar(&ca.ClientsetOnly, "clientset-only", ca.ClientsetOnly, "when set, client-gen only generates the clientset shell, without generating the individual typed clients") + pflag.BoolVar(&ca.FakeClient, "fake-clientset", ca.FakeClient, "when set, client-gen will generate the fake clientset that can be used in tests") + + // support old flags + fs.SetNormalizeFunc(mapFlagName("clientset-path", "output-package", fs.GetNormalizeFunc())) +} + +func Validate(genericArgs *args.GeneratorArgs) error { + customArgs := genericArgs.CustomArgs.(*CustomArgs) + + if len(genericArgs.OutputPackagePath) == 0 { + return fmt.Errorf("output package cannot be empty") + } + if len(customArgs.ClientsetName) == 0 { + return fmt.Errorf("clientset name cannot be empty") + } + if len(customArgs.ClientsetAPIPath) == 0 { + return fmt.Errorf("clientset API path cannot be empty") + } + + return nil +} + +// GroupVersionPackages returns a map from GroupVersion to the package with the types.go. +func (ca *CustomArgs) GroupVersionPackages() map[types.GroupVersion]string { + res := map[types.GroupVersion]string{} + for _, pkg := range ca.Groups { + for _, v := range pkg.Versions { + res[types.GroupVersion{Group: pkg.Group, Version: v.Version}] = v.Package + } + } + return res +} + +func mapFlagName(from, to string, old func(fs *pflag.FlagSet, name string) pflag.NormalizedName) func(fs *pflag.FlagSet, name string) pflag.NormalizedName { + return func(fs *pflag.FlagSet, name string) pflag.NormalizedName { + if name == from { + name = to + } + return old(fs, name) + } +} diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/args/gvpackages.go b/vendor/k8s.io/code-generator/cmd/client-gen/args/gvpackages.go new file mode 100644 index 0000000000..8da71d6f9b --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/args/gvpackages.go @@ -0,0 +1,183 @@ +/* +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 args + +import ( + "bytes" + "encoding/csv" + "flag" + "path" + "sort" + "strings" + + "k8s.io/code-generator/cmd/client-gen/types" +) + +type inputBasePathValue struct { + builder *groupVersionsBuilder +} + +var _ flag.Value = &inputBasePathValue{} + +func NewInputBasePathValue(builder *groupVersionsBuilder, def string) *inputBasePathValue { + v := &inputBasePathValue{ + builder: builder, + } + v.Set(def) + return v +} + +func (s *inputBasePathValue) Set(val string) error { + s.builder.importBasePath = val + return s.builder.update() +} + +func (s *inputBasePathValue) Type() string { + return "string" +} + +func (s *inputBasePathValue) String() string { + return s.builder.importBasePath +} + +type gvPackagesValue struct { + builder *groupVersionsBuilder + groups []string + changed bool +} + +func NewGVPackagesValue(builder *groupVersionsBuilder, def []string) *gvPackagesValue { + gvp := new(gvPackagesValue) + gvp.builder = builder + if def != nil { + if err := gvp.set(def); err != nil { + panic(err) + } + } + return gvp +} + +var _ flag.Value = &gvPackagesValue{} + +func (s *gvPackagesValue) set(vs []string) error { + if s.changed { + s.groups = append(s.groups, vs...) + } else { + s.groups = append([]string(nil), vs...) + } + + s.builder.groups = s.groups + return s.builder.update() +} + +func (s *gvPackagesValue) Set(val string) error { + vs, err := readAsCSV(val) + if err != nil { + return err + } + if err := s.set(vs); err != nil { + return err + } + s.changed = true + return nil +} + +func (s *gvPackagesValue) Type() string { + return "stringSlice" +} + +func (s *gvPackagesValue) String() string { + str, _ := writeAsCSV(s.groups) + return "[" + str + "]" +} + +type groupVersionsBuilder struct { + value *[]types.GroupVersions + groups []string + importBasePath string +} + +func NewGroupVersionsBuilder(groups *[]types.GroupVersions) *groupVersionsBuilder { + return &groupVersionsBuilder{ + value: groups, + } +} + +func (p *groupVersionsBuilder) update() error { + var seenGroups = make(map[types.Group]*types.GroupVersions) + for _, v := range p.groups { + pth, gvString := parsePathGroupVersion(v) + gv, err := types.ToGroupVersion(gvString) + if err != nil { + return err + } + + versionPkg := types.PackageVersion{Package: path.Join(p.importBasePath, pth, gv.Group.NonEmpty(), gv.Version.String()), Version: gv.Version} + if group, ok := seenGroups[gv.Group]; ok { + seenGroups[gv.Group].Versions = append(group.Versions, versionPkg) + } else { + seenGroups[gv.Group] = &types.GroupVersions{ + PackageName: gv.Group.NonEmpty(), + Group: gv.Group, + Versions: []types.PackageVersion{versionPkg}, + } + } + } + + var groupNames []string + for groupName := range seenGroups { + groupNames = append(groupNames, groupName.String()) + } + sort.Strings(groupNames) + *p.value = []types.GroupVersions{} + for _, groupName := range groupNames { + *p.value = append(*p.value, *seenGroups[types.Group(groupName)]) + } + + return nil +} + +func parsePathGroupVersion(pgvString string) (gvPath string, gvString string) { + subs := strings.Split(pgvString, "/") + length := len(subs) + switch length { + case 0, 1, 2: + return "", pgvString + default: + return strings.Join(subs[:length-2], "/"), strings.Join(subs[length-2:], "/") + } +} + +func readAsCSV(val string) ([]string, error) { + if val == "" { + return []string{}, nil + } + stringReader := strings.NewReader(val) + csvReader := csv.NewReader(stringReader) + return csvReader.Read() +} + +func writeAsCSV(vals []string) (string, error) { + b := &bytes.Buffer{} + w := csv.NewWriter(b) + err := w.Write(vals) + if err != nil { + return "", err + } + w.Flush() + return strings.TrimSuffix(b.String(), "\n"), nil +} diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/args/gvtype.go b/vendor/k8s.io/code-generator/cmd/client-gen/args/gvtype.go new file mode 100644 index 0000000000..e4e3ccb536 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/args/gvtype.go @@ -0,0 +1,110 @@ +/* +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 args + +import ( + "flag" + "fmt" + "strings" + + "k8s.io/code-generator/cmd/client-gen/types" +) + +type gvTypeValue struct { + gvToTypes *map[types.GroupVersion][]string + changed bool +} + +func NewGVTypesValue(gvToTypes *map[types.GroupVersion][]string, def []string) *gvTypeValue { + gvt := new(gvTypeValue) + gvt.gvToTypes = gvToTypes + if def != nil { + if err := gvt.set(def); err != nil { + panic(err) + } + } + return gvt +} + +var _ flag.Value = &gvTypeValue{} + +func (s *gvTypeValue) set(vs []string) error { + if !s.changed { + *s.gvToTypes = map[types.GroupVersion][]string{} + } + + for _, input := range vs { + gvString, typeStr, err := parseGroupVersionType(input) + if err != nil { + return err + } + gv, err := types.ToGroupVersion(gvString) + if err != nil { + return err + } + types, ok := (*s.gvToTypes)[gv] + if !ok { + types = []string{} + } + types = append(types, typeStr) + (*s.gvToTypes)[gv] = types + } + + return nil +} + +func (s *gvTypeValue) Set(val string) error { + vs, err := readAsCSV(val) + if err != nil { + return err + } + if err := s.set(vs); err != nil { + return err + } + s.changed = true + return nil +} + +func (s *gvTypeValue) Type() string { + return "stringSlice" +} + +func (s *gvTypeValue) String() string { + strs := make([]string, 0, len(*s.gvToTypes)) + for gv, ts := range *s.gvToTypes { + for _, t := range ts { + strs = append(strs, gv.Group.String()+"/"+gv.Version.String()+"/"+t) + } + } + str, _ := writeAsCSV(strs) + return "[" + str + "]" +} + +func parseGroupVersionType(gvtString string) (gvString string, typeStr string, err error) { + invalidFormatErr := fmt.Errorf("invalid value: %s, should be of the form group/version/type", gvtString) + subs := strings.Split(gvtString, "/") + length := len(subs) + switch length { + case 2: + // gvtString of the form group/type, e.g. api/Service,extensions/ReplicaSet + return subs[0] + "/", subs[1], nil + case 3: + return strings.Join(subs[:length-1], "/"), subs[length-1], nil + default: + return "", "", invalidFormatErr + } +} diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/client_generator.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/client_generator.go new file mode 100644 index 0000000000..335e995c0a --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/client_generator.go @@ -0,0 +1,426 @@ +/* +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 generators has the generators for the client-gen utility. +package generators + +import ( + "path/filepath" + "strings" + + clientgenargs "k8s.io/code-generator/cmd/client-gen/args" + "k8s.io/code-generator/cmd/client-gen/generators/fake" + "k8s.io/code-generator/cmd/client-gen/generators/scheme" + "k8s.io/code-generator/cmd/client-gen/generators/util" + "k8s.io/code-generator/cmd/client-gen/path" + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + "k8s.io/gengo/args" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "github.com/golang/glog" +) + +// NameSystems returns the name system used by the generators in this package. +func NameSystems() namer.NameSystems { + pluralExceptions := map[string]string{ + "Endpoints": "Endpoints", + } + lowercaseNamer := namer.NewAllLowercasePluralNamer(pluralExceptions) + + publicNamer := &ExceptionNamer{ + Exceptions: map[string]string{ + // these exceptions are used to deconflict the generated code + // you can put your fully qualified package like + // to generate a name that doesn't conflict with your group. + // "k8s.io/apis/events/v1beta1.Event": "EventResource" + }, + KeyFunc: func(t *types.Type) string { + return t.Name.Package + "." + t.Name.Name + }, + Delegate: namer.NewPublicNamer(0), + } + privateNamer := &ExceptionNamer{ + Exceptions: map[string]string{ + // these exceptions are used to deconflict the generated code + // you can put your fully qualified package like + // to generate a name that doesn't conflict with your group. + // "k8s.io/apis/events/v1beta1.Event": "eventResource" + }, + KeyFunc: func(t *types.Type) string { + return t.Name.Package + "." + t.Name.Name + }, + Delegate: namer.NewPrivateNamer(0), + } + publicPluralNamer := &ExceptionNamer{ + Exceptions: map[string]string{ + // these exceptions are used to deconflict the generated code + // you can put your fully qualified package like + // to generate a name that doesn't conflict with your group. + // "k8s.io/apis/events/v1beta1.Event": "EventResource" + }, + KeyFunc: func(t *types.Type) string { + return t.Name.Package + "." + t.Name.Name + }, + Delegate: namer.NewPublicPluralNamer(pluralExceptions), + } + privatePluralNamer := &ExceptionNamer{ + Exceptions: map[string]string{ + // you can put your fully qualified package like + // to generate a name that doesn't conflict with your group. + // "k8s.io/apis/events/v1beta1.Event": "eventResource" + // these exceptions are used to deconflict the generated code + "k8s.io/apis/events/v1beta1.Event": "eventResources", + "k8s.io/kubernetes/pkg/apis/events.Event": "eventResources", + }, + KeyFunc: func(t *types.Type) string { + return t.Name.Package + "." + t.Name.Name + }, + Delegate: namer.NewPrivatePluralNamer(pluralExceptions), + } + + return namer.NameSystems{ + "singularKind": namer.NewPublicNamer(0), + "public": publicNamer, + "private": privateNamer, + "raw": namer.NewRawNamer("", nil), + "publicPlural": publicPluralNamer, + "privatePlural": privatePluralNamer, + "allLowercasePlural": lowercaseNamer, + "resource": NewTagOverrideNamer("resourceName", lowercaseNamer), + } +} + +// ExceptionNamer allows you specify exceptional cases with exact names. This allows you to have control +// for handling various conflicts, like group and resource names for instance. +type ExceptionNamer struct { + Exceptions map[string]string + KeyFunc func(*types.Type) string + + Delegate namer.Namer +} + +// Name provides the requested name for a type. +func (n *ExceptionNamer) Name(t *types.Type) string { + key := n.KeyFunc(t) + if exception, ok := n.Exceptions[key]; ok { + return exception + } + return n.Delegate.Name(t) +} + +// DefaultNameSystem returns the default name system for ordering the types to be +// processed by the generators in this package. +func DefaultNameSystem() string { + return "public" +} + +func packageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, clientsetPackage string, groupPackageName string, groupGoName string, apiPath string, srcTreePath string, inputPackage string, boilerplate []byte) generator.Package { + groupVersionClientPackage := strings.ToLower(filepath.Join(clientsetPackage, "typed", groupPackageName, gv.Version.NonEmpty())) + return &generator.DefaultPackage{ + PackageName: strings.ToLower(gv.Version.NonEmpty()), + PackagePath: groupVersionClientPackage, + HeaderText: boilerplate, + PackageDocumentation: []byte( + `// This package has the automatically generated typed clients. +`), + // GeneratorFunc returns a list of generators. Each generator makes a + // single file. + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = []generator.Generator{ + // Always generate a "doc.go" file. + generator.DefaultGen{OptionalName: "doc"}, + } + // Since we want a file per type that we generate a client for, we + // have to provide a function for this. + for _, t := range typeList { + generators = append(generators, &genClientForType{ + DefaultGen: generator.DefaultGen{ + OptionalName: strings.ToLower(c.Namers["private"].Name(t)), + }, + outputPackage: groupVersionClientPackage, + clientsetPackage: clientsetPackage, + group: gv.Group.NonEmpty(), + version: gv.Version.String(), + groupGoName: groupGoName, + typeToMatch: t, + imports: generator.NewImportTracker(), + }) + } + + generators = append(generators, &genGroup{ + DefaultGen: generator.DefaultGen{ + OptionalName: groupPackageName + "_client", + }, + outputPackage: groupVersionClientPackage, + inputPackage: inputPackage, + clientsetPackage: clientsetPackage, + group: gv.Group.NonEmpty(), + version: gv.Version.String(), + groupGoName: groupGoName, + apiPath: apiPath, + types: typeList, + imports: generator.NewImportTracker(), + }) + + expansionFileName := "generated_expansion" + generators = append(generators, &genExpansion{ + groupPackagePath: filepath.Join(srcTreePath, groupVersionClientPackage), + DefaultGen: generator.DefaultGen{ + OptionalName: expansionFileName, + }, + types: typeList, + }) + + return generators + }, + FilterFunc: func(c *generator.Context, t *types.Type) bool { + return util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)).GenerateClient + }, + } +} + +func packageForClientset(customArgs *clientgenargs.CustomArgs, clientsetPackage string, groupGoNames map[clientgentypes.GroupVersion]string, boilerplate []byte) generator.Package { + return &generator.DefaultPackage{ + PackageName: customArgs.ClientsetName, + PackagePath: clientsetPackage, + HeaderText: boilerplate, + PackageDocumentation: []byte( + `// This package has the automatically generated clientset. +`), + // GeneratorFunc returns a list of generators. Each generator generates a + // single file. + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = []generator.Generator{ + // Always generate a "doc.go" file. + generator.DefaultGen{OptionalName: "doc"}, + + &genClientset{ + DefaultGen: generator.DefaultGen{ + OptionalName: "clientset", + }, + groups: customArgs.Groups, + groupGoNames: groupGoNames, + clientsetPackage: clientsetPackage, + outputPackage: customArgs.ClientsetName, + imports: generator.NewImportTracker(), + }, + } + return generators + }, + } +} + +func packageForScheme(customArgs *clientgenargs.CustomArgs, clientsetPackage string, srcTreePath string, groupGoNames map[clientgentypes.GroupVersion]string, boilerplate []byte) generator.Package { + schemePackage := filepath.Join(clientsetPackage, "scheme") + + // create runtime.Registry for internal client because it has to know about group versions + internalClient := false +NextGroup: + for _, group := range customArgs.Groups { + for _, v := range group.Versions { + if v.String() == "" { + internalClient = true + break NextGroup + } + } + } + + return &generator.DefaultPackage{ + PackageName: "scheme", + PackagePath: schemePackage, + HeaderText: boilerplate, + PackageDocumentation: []byte( + `// This package contains the scheme of the automatically generated clientset. +`), + // GeneratorFunc returns a list of generators. Each generator generates a + // single file. + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = []generator.Generator{ + // Always generate a "doc.go" file. + generator.DefaultGen{OptionalName: "doc"}, + + &scheme.GenScheme{ + DefaultGen: generator.DefaultGen{ + OptionalName: "register", + }, + InputPackages: customArgs.GroupVersionPackages(), + OutputPackage: schemePackage, + OutputPath: filepath.Join(srcTreePath, schemePackage), + Groups: customArgs.Groups, + GroupGoNames: groupGoNames, + ImportTracker: generator.NewImportTracker(), + CreateRegistry: internalClient, + }, + } + return generators + }, + } +} + +// applyGroupOverrides applies group name overrides to each package, if applicable. If there is a +// comment of the form "// +groupName=somegroup" or "// +groupName=somegroup.foo.bar.io", use the +// first field (somegroup) as the name of the group in Go code, e.g. as the func name in a clientset. +// +// If the first field of the groupName is not unique within the clientset, use "// +groupName=unique +func applyGroupOverrides(universe types.Universe, customArgs *clientgenargs.CustomArgs) { + // Create a map from "old GV" to "new GV" so we know what changes we need to make. + changes := make(map[clientgentypes.GroupVersion]clientgentypes.GroupVersion) + for gv, inputDir := range customArgs.GroupVersionPackages() { + p := universe.Package(inputDir) + if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil { + newGV := clientgentypes.GroupVersion{ + Group: clientgentypes.Group(override[0]), + Version: gv.Version, + } + changes[gv] = newGV + } + } + + // Modify customArgs.Groups based on the groupName overrides. + newGroups := make([]clientgentypes.GroupVersions, 0, len(customArgs.Groups)) + for _, gvs := range customArgs.Groups { + gv := clientgentypes.GroupVersion{ + Group: gvs.Group, + Version: gvs.Versions[0].Version, // we only need a version, and the first will do + } + if newGV, ok := changes[gv]; ok { + // There's an override, so use it. + newGVS := clientgentypes.GroupVersions{ + PackageName: gvs.PackageName, + Group: newGV.Group, + Versions: gvs.Versions, + } + newGroups = append(newGroups, newGVS) + } else { + // No override. + newGroups = append(newGroups, gvs) + } + } + customArgs.Groups = newGroups +} + +// Packages makes the client package definition. +func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { + boilerplate, err := arguments.LoadGoBoilerplate() + if err != nil { + glog.Fatalf("Failed loading boilerplate: %v", err) + } + + customArgs, ok := arguments.CustomArgs.(*clientgenargs.CustomArgs) + if !ok { + glog.Fatalf("cannot convert arguments.CustomArgs to clientgenargs.CustomArgs") + } + includedTypesOverrides := customArgs.IncludedTypesOverrides + + applyGroupOverrides(context.Universe, customArgs) + + gvToTypes := map[clientgentypes.GroupVersion][]*types.Type{} + groupGoNames := make(map[clientgentypes.GroupVersion]string) + for gv, inputDir := range customArgs.GroupVersionPackages() { + p := context.Universe.Package(path.Vendorless(inputDir)) + + // If there's a comment of the form "// +groupGoName=SomeUniqueShortName", use that as + // the Go group identifier in CamelCase. It defaults + groupGoNames[gv] = namer.IC(strings.Split(gv.Group.NonEmpty(), ".")[0]) + if override := types.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil { + groupGoNames[gv] = namer.IC(override[0]) + } + + // Package are indexed with the vendor prefix stripped + for n, t := range p.Types { + // filter out types which are not included in user specified overrides. + typesOverride, ok := includedTypesOverrides[gv] + if ok { + found := false + for _, typeStr := range typesOverride { + if typeStr == n { + found = true + break + } + } + if !found { + continue + } + } else { + // User has not specified any override for this group version. + // filter out types which dont have genclient. + if tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)); !tags.GenerateClient { + continue + } + } + if _, found := gvToTypes[gv]; !found { + gvToTypes[gv] = []*types.Type{} + } + gvToTypes[gv] = append(gvToTypes[gv], t) + } + } + + var packageList []generator.Package + clientsetPackage := filepath.Join(arguments.OutputPackagePath, customArgs.ClientsetName) + + packageList = append(packageList, packageForClientset(customArgs, clientsetPackage, groupGoNames, boilerplate)) + packageList = append(packageList, packageForScheme(customArgs, clientsetPackage, arguments.OutputBase, groupGoNames, boilerplate)) + if customArgs.FakeClient { + packageList = append(packageList, fake.PackageForClientset(customArgs, clientsetPackage, groupGoNames, boilerplate)) + } + + // If --clientset-only=true, we don't regenerate the individual typed clients. + if customArgs.ClientsetOnly { + return generator.Packages(packageList) + } + + orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)} + gvPackages := customArgs.GroupVersionPackages() + for _, group := range customArgs.Groups { + for _, version := range group.Versions { + gv := clientgentypes.GroupVersion{Group: group.Group, Version: version.Version} + types := gvToTypes[gv] + inputPath := gvPackages[gv] + packageList = append(packageList, packageForGroup(gv, orderer.OrderTypes(types), clientsetPackage, group.PackageName, groupGoNames[gv], customArgs.ClientsetAPIPath, arguments.OutputBase, inputPath, boilerplate)) + if customArgs.FakeClient { + packageList = append(packageList, fake.PackageForGroup(gv, orderer.OrderTypes(types), clientsetPackage, group.PackageName, groupGoNames[gv], inputPath, boilerplate)) + } + } + } + + return generator.Packages(packageList) +} + +// tagOverrideNamer is a namer which pulls names from a given tag, if specified, +// and otherwise falls back to a different namer. +type tagOverrideNamer struct { + tagName string + fallback namer.Namer +} + +func (n *tagOverrideNamer) Name(t *types.Type) string { + if nameOverride := extractTag(n.tagName, append(t.SecondClosestCommentLines, t.CommentLines...)); nameOverride != "" { + return nameOverride + } + + return n.fallback.Name(t) +} + +// NewTagOverrideNamer creates a namer.Namer which uses the contents of the given tag as +// the name, or falls back to another Namer if the tag is not present. +func NewTagOverrideNamer(tagName string, fallback namer.Namer) namer.Namer { + return &tagOverrideNamer{ + tagName: tagName, + fallback: fallback, + } +} diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/fake_client_generator.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/fake_client_generator.go new file mode 100644 index 0000000000..ec439c2f7a --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/fake_client_generator.go @@ -0,0 +1,130 @@ +/* +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 fake + +import ( + "path/filepath" + "strings" + + "k8s.io/gengo/generator" + "k8s.io/gengo/types" + + clientgenargs "k8s.io/code-generator/cmd/client-gen/args" + scheme "k8s.io/code-generator/cmd/client-gen/generators/scheme" + "k8s.io/code-generator/cmd/client-gen/generators/util" + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" +) + +func PackageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, clientsetPackage string, groupPackageName string, groupGoName string, inputPackage string, boilerplate []byte) generator.Package { + outputPackage := strings.ToLower(filepath.Join(clientsetPackage, "typed", groupPackageName, gv.Version.NonEmpty(), "fake")) + // TODO: should make this a function, called by here and in client-generator.go + realClientPackage := filepath.Join(clientsetPackage, "typed", groupPackageName, gv.Version.NonEmpty()) + return &generator.DefaultPackage{ + PackageName: "fake", + PackagePath: outputPackage, + HeaderText: boilerplate, + PackageDocumentation: []byte( + `// Package fake has the automatically generated clients. +`), + // GeneratorFunc returns a list of generators. Each generator makes a + // single file. + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = []generator.Generator{ + // Always generate a "doc.go" file. + generator.DefaultGen{OptionalName: "doc"}, + } + // Since we want a file per type that we generate a client for, we + // have to provide a function for this. + for _, t := range typeList { + generators = append(generators, &genFakeForType{ + DefaultGen: generator.DefaultGen{ + OptionalName: "fake_" + strings.ToLower(c.Namers["private"].Name(t)), + }, + outputPackage: outputPackage, + inputPackage: inputPackage, + group: gv.Group.NonEmpty(), + version: gv.Version.String(), + groupGoName: groupGoName, + typeToMatch: t, + imports: generator.NewImportTracker(), + }) + } + + generators = append(generators, &genFakeForGroup{ + DefaultGen: generator.DefaultGen{ + OptionalName: "fake_" + groupPackageName + "_client", + }, + outputPackage: outputPackage, + realClientPackage: realClientPackage, + group: gv.Group.NonEmpty(), + version: gv.Version.String(), + groupGoName: groupGoName, + types: typeList, + imports: generator.NewImportTracker(), + }) + return generators + }, + FilterFunc: func(c *generator.Context, t *types.Type) bool { + return util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)).GenerateClient + }, + } +} + +func PackageForClientset(customArgs *clientgenargs.CustomArgs, clientsetPackage string, groupGoNames map[clientgentypes.GroupVersion]string, boilerplate []byte) generator.Package { + return &generator.DefaultPackage{ + // TODO: we'll generate fake clientset for different release in the future. + // Package name and path are hard coded for now. + PackageName: "fake", + PackagePath: filepath.Join(clientsetPackage, "fake"), + HeaderText: boilerplate, + PackageDocumentation: []byte( + `// This package has the automatically generated fake clientset. +`), + // GeneratorFunc returns a list of generators. Each generator generates a + // single file. + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = []generator.Generator{ + // Always generate a "doc.go" file. + generator.DefaultGen{OptionalName: "doc"}, + + &genClientset{ + DefaultGen: generator.DefaultGen{ + OptionalName: "clientset_generated", + }, + groups: customArgs.Groups, + groupGoNames: groupGoNames, + fakeClientsetPackage: clientsetPackage, + outputPackage: "fake", + imports: generator.NewImportTracker(), + realClientsetPackage: clientsetPackage, + }, + &scheme.GenScheme{ + DefaultGen: generator.DefaultGen{ + OptionalName: "register", + }, + InputPackages: customArgs.GroupVersionPackages(), + OutputPackage: clientsetPackage, + Groups: customArgs.Groups, + GroupGoNames: groupGoNames, + ImportTracker: generator.NewImportTracker(), + PrivateScheme: true, + }, + } + return generators + }, + } +} diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/generator_fake_for_clientset.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/generator_fake_for_clientset.go new file mode 100644 index 0000000000..f1225acb3d --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/generator_fake_for_clientset.go @@ -0,0 +1,173 @@ +/* +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 fake + +import ( + "fmt" + "io" + "path/filepath" + "strings" + + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" +) + +// genClientset generates a package for a clientset. +type genClientset struct { + generator.DefaultGen + groups []clientgentypes.GroupVersions + groupGoNames map[clientgentypes.GroupVersion]string + fakeClientsetPackage string + outputPackage string + imports namer.ImportTracker + clientsetGenerated bool + // the import path of the generated real clientset. + realClientsetPackage string +} + +var _ generator.Generator = &genClientset{} + +func (g *genClientset) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +// We only want to call GenerateType() once. +func (g *genClientset) Filter(c *generator.Context, t *types.Type) bool { + ret := !g.clientsetGenerated + g.clientsetGenerated = true + return ret +} + +func (g *genClientset) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.imports.ImportLines()...) + for _, group := range g.groups { + for _, version := range group.Versions { + groupClientPackage := filepath.Join(g.fakeClientsetPackage, "typed", group.PackageName, version.NonEmpty()) + fakeGroupClientPackage := filepath.Join(groupClientPackage, "fake") + + groupAlias := strings.ToLower(g.groupGoNames[clientgentypes.GroupVersion{Group: group.Group, Version: version.Version}]) + imports = append(imports, strings.ToLower(fmt.Sprintf("%s%s \"%s\"", groupAlias, version.NonEmpty(), groupClientPackage))) + imports = append(imports, strings.ToLower(fmt.Sprintf("fake%s%s \"%s\"", groupAlias, version.NonEmpty(), fakeGroupClientPackage))) + } + } + // the package that has the clientset Interface + imports = append(imports, fmt.Sprintf("clientset \"%s\"", g.realClientsetPackage)) + // imports for the code in commonTemplate + imports = append(imports, + "k8s.io/client-go/testing", + "k8s.io/client-go/discovery", + "fakediscovery \"k8s.io/client-go/discovery/fake\"", + "k8s.io/apimachinery/pkg/runtime", + "k8s.io/apimachinery/pkg/watch", + ) + + return +} + +func (g *genClientset) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + // TODO: We actually don't need any type information to generate the clientset, + // perhaps we can adapt the go2ild framework to this kind of usage. + sw := generator.NewSnippetWriter(w, c, "$", "$") + + allGroups := clientgentypes.ToGroupVersionInfo(g.groups, g.groupGoNames) + + sw.Do(common, nil) + sw.Do(checkImpl, nil) + + for _, group := range allGroups { + m := map[string]interface{}{ + "group": group.Group, + "version": group.Version, + "PackageAlias": group.PackageAlias, + "GroupGoName": group.GroupGoName, + "Version": namer.IC(group.Version.String()), + } + + sw.Do(clientsetInterfaceImplTemplate, m) + // don't generated the default method if generating internalversion clientset + if group.IsDefaultVersion && group.Version != "" { + sw.Do(clientsetInterfaceDefaultVersionImpl, m) + } + } + + return sw.Error() +} + +// This part of code is version-independent, unchanging. +var common = ` +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} +` + +var checkImpl = ` +var _ clientset.Interface = &Clientset{} +` + +var clientsetInterfaceImplTemplate = ` +// $.GroupGoName$$.Version$ retrieves the $.GroupGoName$$.Version$Client +func (c *Clientset) $.GroupGoName$$.Version$() $.PackageAlias$.$.GroupGoName$$.Version$Interface { + return &fake$.PackageAlias$.Fake$.GroupGoName$$.Version${Fake: &c.Fake} +} +` + +var clientsetInterfaceDefaultVersionImpl = ` +// $.GroupGoName$ retrieves the $.GroupGoName$$.Version$Client +func (c *Clientset) $.GroupGoName$() $.PackageAlias$.$.GroupGoName$$.Version$Interface { + return &fake$.PackageAlias$.Fake$.GroupGoName$$.Version${Fake: &c.Fake} +} +` diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/generator_fake_for_group.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/generator_fake_for_group.go new file mode 100644 index 0000000000..675fa5f6f7 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/generator_fake_for_group.go @@ -0,0 +1,130 @@ +/* +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 fake + +import ( + "fmt" + "io" + "path/filepath" + "strings" + + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/code-generator/cmd/client-gen/generators/util" +) + +// genFakeForGroup produces a file for a group client, e.g. ExtensionsClient for the extension group. +type genFakeForGroup struct { + generator.DefaultGen + outputPackage string + realClientPackage string + group string + version string + groupGoName string + // types in this group + types []*types.Type + imports namer.ImportTracker + // If the genGroup has been called. This generator should only execute once. + called bool +} + +var _ generator.Generator = &genFakeForGroup{} + +// We only want to call GenerateType() once per group. +func (g *genFakeForGroup) Filter(c *generator.Context, t *types.Type) bool { + if !g.called { + g.called = true + return true + } + return false +} + +func (g *genFakeForGroup) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *genFakeForGroup) Imports(c *generator.Context) (imports []string) { + imports = g.imports.ImportLines() + if len(g.types) != 0 { + imports = append(imports, strings.ToLower(fmt.Sprintf("%s \"%s\"", filepath.Base(g.realClientPackage), g.realClientPackage))) + } + return imports +} + +func (g *genFakeForGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + + m := map[string]interface{}{ + "GroupGoName": g.groupGoName, + "Version": namer.IC(g.version), + "Fake": c.Universe.Type(types.Name{Package: "k8s.io/client-go/testing", Name: "Fake"}), + "RESTClientInterface": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Interface"}), + "RESTClient": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "RESTClient"}), + } + + sw.Do(groupClientTemplate, m) + for _, t := range g.types { + tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + if err != nil { + return err + } + wrapper := map[string]interface{}{ + "type": t, + "GroupGoName": g.groupGoName, + "Version": namer.IC(g.version), + "realClientPackage": strings.ToLower(filepath.Base(g.realClientPackage)), + } + if tags.NonNamespaced { + sw.Do(getterImplNonNamespaced, wrapper) + continue + } + sw.Do(getterImplNamespaced, wrapper) + } + sw.Do(getRESTClient, m) + return sw.Error() +} + +var groupClientTemplate = ` +type Fake$.GroupGoName$$.Version$ struct { + *$.Fake|raw$ +} +` + +var getterImplNamespaced = ` +func (c *Fake$.GroupGoName$$.Version$) $.type|publicPlural$(namespace string) $.realClientPackage$.$.type|public$Interface { + return &Fake$.type|publicPlural${c, namespace} +} +` + +var getterImplNonNamespaced = ` +func (c *Fake$.GroupGoName$$.Version$) $.type|publicPlural$() $.realClientPackage$.$.type|public$Interface { + return &Fake$.type|publicPlural${c} +} +` + +var getRESTClient = ` +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *Fake$.GroupGoName$$.Version$) RESTClient() $.RESTClientInterface|raw$ { + var ret *$.RESTClient|raw$ + return ret +} +` diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/generator_fake_for_type.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/generator_fake_for_type.go new file mode 100644 index 0000000000..bf18c14c64 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/fake/generator_fake_for_type.go @@ -0,0 +1,479 @@ +/* +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 fake + +import ( + "io" + "path/filepath" + "strings" + + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/code-generator/cmd/client-gen/generators/util" + "k8s.io/code-generator/cmd/client-gen/path" +) + +// genFakeForType produces a file for each top-level type. +type genFakeForType struct { + generator.DefaultGen + outputPackage string + group string + version string + groupGoName string + inputPackage string + typeToMatch *types.Type + imports namer.ImportTracker +} + +var _ generator.Generator = &genFakeForType{} + +// Filter ignores all but one type because we're making a single file per type. +func (g *genFakeForType) Filter(c *generator.Context, t *types.Type) bool { return t == g.typeToMatch } + +func (g *genFakeForType) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *genFakeForType) Imports(c *generator.Context) (imports []string) { + return g.imports.ImportLines() +} + +// Ideally, we'd like genStatus to return true if there is a subresource path +// registered for "status" in the API server, but we do not have that +// information, so genStatus returns true if the type has a status field. +func genStatus(t *types.Type) bool { + // Default to true if we have a Status member + hasStatus := false + for _, m := range t.Members { + if m.Name == "Status" { + hasStatus = true + break + } + } + + tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + return hasStatus && !tags.NoStatus +} + +// hasObjectMeta returns true if the type has a ObjectMeta field. +func hasObjectMeta(t *types.Type) bool { + for _, m := range t.Members { + if m.Embedded == true && m.Name == "ObjectMeta" { + return true + } + } + return false +} + +// GenerateType makes the body of a file implementing the individual typed client for type t. +func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + pkg := filepath.Base(t.Name.Package) + tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + if err != nil { + return err + } + canonicalGroup := g.group + if canonicalGroup == "core" { + canonicalGroup = "" + } + + groupName := g.group + if g.group == "core" { + groupName = "" + } + + // allow user to define a group name that's different from the one parsed from the directory. + p := c.Universe.Package(path.Vendorless(g.inputPackage)) + if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil { + groupName = override[0] + } + + const pkgClientGoTesting = "k8s.io/client-go/testing" + m := map[string]interface{}{ + "type": t, + "inputType": t, + "resultType": t, + "subresourcePath": "", + "package": pkg, + "Package": namer.IC(pkg), + "namespaced": !tags.NonNamespaced, + "Group": namer.IC(g.group), + "GroupGoName": g.groupGoName, + "Version": namer.IC(g.version), + "group": canonicalGroup, + "groupName": groupName, + "version": g.version, + "DeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "DeleteOptions"}), + "ListOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ListOptions"}), + "GetOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "GetOptions"}), + "Everything": c.Universe.Function(types.Name{Package: "k8s.io/apimachinery/pkg/labels", Name: "Everything"}), + "GroupVersionResource": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime/schema", Name: "GroupVersionResource"}), + "GroupVersionKind": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime/schema", Name: "GroupVersionKind"}), + "PatchType": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/types", Name: "PatchType"}), + "watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/watch", Name: "Interface"}), + + "NewRootListAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootListAction"}), + "NewListAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewListAction"}), + "NewRootGetAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootGetAction"}), + "NewGetAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewGetAction"}), + "NewRootDeleteAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootDeleteAction"}), + "NewDeleteAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewDeleteAction"}), + "NewRootDeleteCollectionAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootDeleteCollectionAction"}), + "NewDeleteCollectionAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewDeleteCollectionAction"}), + "NewRootUpdateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootUpdateAction"}), + "NewUpdateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewUpdateAction"}), + "NewRootCreateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootCreateAction"}), + "NewCreateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewCreateAction"}), + "NewRootWatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootWatchAction"}), + "NewWatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewWatchAction"}), + "NewCreateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewCreateSubresourceAction"}), + "NewRootCreateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootCreateSubresourceAction"}), + "NewUpdateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewUpdateSubresourceAction"}), + "NewGetSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewGetSubresourceAction"}), + "NewRootGetSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootGetSubresourceAction"}), + "NewRootUpdateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootUpdateSubresourceAction"}), + "NewRootPatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootPatchAction"}), + "NewPatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewPatchAction"}), + "NewRootPatchSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootPatchSubresourceAction"}), + "NewPatchSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewPatchSubresourceAction"}), + "ExtractFromListOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "ExtractFromListOptions"}), + } + + if tags.NonNamespaced { + sw.Do(structNonNamespaced, m) + } else { + sw.Do(structNamespaced, m) + } + + if tags.NoVerbs { + return sw.Error() + } + sw.Do(resource, m) + sw.Do(kind, m) + + if tags.HasVerb("get") { + sw.Do(getTemplate, m) + } + if tags.HasVerb("list") { + if hasObjectMeta(t) { + sw.Do(listUsingOptionsTemplate, m) + } else { + sw.Do(listTemplate, m) + } + } + if tags.HasVerb("watch") { + sw.Do(watchTemplate, m) + } + + if tags.HasVerb("create") { + sw.Do(createTemplate, m) + } + if tags.HasVerb("update") { + sw.Do(updateTemplate, m) + } + if tags.HasVerb("updateStatus") && genStatus(t) { + sw.Do(updateStatusTemplate, m) + } + if tags.HasVerb("delete") { + sw.Do(deleteTemplate, m) + } + if tags.HasVerb("deleteCollection") { + sw.Do(deleteCollectionTemplate, m) + } + if tags.HasVerb("patch") { + sw.Do(patchTemplate, m) + } + + // generate extended client methods + for _, e := range tags.Extensions { + inputType := *t + resultType := *t + if len(e.InputTypeOverride) > 0 { + if name, pkg := e.Input(); len(pkg) > 0 { + newType := c.Universe.Type(types.Name{Package: pkg, Name: name}) + inputType = *newType + } else { + inputType.Name.Name = e.InputTypeOverride + } + } + if len(e.ResultTypeOverride) > 0 { + if name, pkg := e.Result(); len(pkg) > 0 { + newType := c.Universe.Type(types.Name{Package: pkg, Name: name}) + resultType = *newType + } else { + resultType.Name.Name = e.ResultTypeOverride + } + } + m["inputType"] = &inputType + m["resultType"] = &resultType + m["subresourcePath"] = e.SubResourcePath + + if e.HasVerb("get") { + if e.IsSubresource() { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, getSubresourceTemplate), m) + } else { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, getTemplate), m) + } + } + + if e.HasVerb("list") { + + sw.Do(adjustTemplate(e.VerbName, e.VerbType, listTemplate), m) + } + + // TODO: Figure out schemantic for watching a sub-resource. + if e.HasVerb("watch") { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, watchTemplate), m) + } + + if e.HasVerb("create") { + if e.IsSubresource() { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, createSubresourceTemplate), m) + } else { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, createTemplate), m) + } + } + + if e.HasVerb("update") { + if e.IsSubresource() { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, updateSubresourceTemplate), m) + } else { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, updateTemplate), m) + } + } + + // TODO: Figure out schemantic for deleting a sub-resource (what arguments + // are passed, does it need two names? etc. + if e.HasVerb("delete") { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, deleteTemplate), m) + } + + if e.HasVerb("patch") { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, patchTemplate), m) + } + } + + return sw.Error() +} + +// adjustTemplate adjust the origin verb template using the expansion name. +// TODO: Make the verbs in templates parametrized so the strings.Replace() is +// not needed. +func adjustTemplate(name, verbType, template string) string { + return strings.Replace(template, " "+strings.Title(verbType), " "+name, -1) +} + +// template for the struct that implements the type's interface +var structNamespaced = ` +// Fake$.type|publicPlural$ implements $.type|public$Interface +type Fake$.type|publicPlural$ struct { + Fake *Fake$.GroupGoName$$.Version$ + ns string +} +` + +// template for the struct that implements the type's interface +var structNonNamespaced = ` +// Fake$.type|publicPlural$ implements $.type|public$Interface +type Fake$.type|publicPlural$ struct { + Fake *Fake$.GroupGoName$$.Version$ +} +` + +var resource = ` +var $.type|allLowercasePlural$Resource = $.GroupVersionResource|raw${Group: "$.groupName$", Version: "$.version$", Resource: "$.type|resource$"} +` + +var kind = ` +var $.type|allLowercasePlural$Kind = $.GroupVersionKind|raw${Group: "$.groupName$", Version: "$.version$", Kind: "$.type|singularKind$"} +` + +var listTemplate = ` +// List takes label and field selectors, and returns the list of $.type|publicPlural$ that match those selectors. +func (c *Fake$.type|publicPlural$) List(opts $.ListOptions|raw$) (result *$.type|raw$List, err error) { + obj, err := c.Fake. + $if .namespaced$Invokes($.NewListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, c.ns, opts), &$.type|raw$List{}) + $else$Invokes($.NewRootListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, opts), &$.type|raw$List{})$end$ + if obj == nil { + return nil, err + } + return obj.(*$.type|raw$List), err +} +` + +var listUsingOptionsTemplate = ` +// List takes label and field selectors, and returns the list of $.type|publicPlural$ that match those selectors. +func (c *Fake$.type|publicPlural$) List(opts $.ListOptions|raw$) (result *$.type|raw$List, err error) { + obj, err := c.Fake. + $if .namespaced$Invokes($.NewListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, c.ns, opts), &$.type|raw$List{}) + $else$Invokes($.NewRootListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, opts), &$.type|raw$List{})$end$ + if obj == nil { + return nil, err + } + + label, _, _ := $.ExtractFromListOptions|raw$(opts) + if label == nil { + label = $.Everything|raw$() + } + list := &$.type|raw$List{ListMeta: obj.(*$.type|raw$List).ListMeta} + for _, item := range obj.(*$.type|raw$List).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} +` + +var getTemplate = ` +// Get takes name of the $.type|private$, and returns the corresponding $.resultType|private$ object, and an error if there is any. +func (c *Fake$.type|publicPlural$) Get(name string, options $.GetOptions|raw$) (result *$.resultType|raw$, err error) { + obj, err := c.Fake. + $if .namespaced$Invokes($.NewGetAction|raw$($.type|allLowercasePlural$Resource, c.ns, name), &$.resultType|raw${}) + $else$Invokes($.NewRootGetAction|raw$($.type|allLowercasePlural$Resource, name), &$.resultType|raw${})$end$ + if obj == nil { + return nil, err + } + return obj.(*$.resultType|raw$), err +} +` + +var getSubresourceTemplate = ` +// Get takes name of the $.type|private$, and returns the corresponding $.resultType|private$ object, and an error if there is any. +func (c *Fake$.type|publicPlural$) Get($.type|private$Name string, options $.GetOptions|raw$) (result *$.resultType|raw$, err error) { + obj, err := c.Fake. + $if .namespaced$Invokes($.NewGetSubresourceAction|raw$($.type|allLowercasePlural$Resource, c.ns, "$.subresourcePath$", $.type|private$Name), &$.resultType|raw${}) + $else$Invokes($.NewRootGetSubresourceAction|raw$($.type|allLowercasePlural$Resource, $.type|private$Name), &$.resultType|raw${})$end$ + if obj == nil { + return nil, err + } + return obj.(*$.resultType|raw$), err +} +` + +var deleteTemplate = ` +// Delete takes name of the $.type|private$ and deletes it. Returns an error if one occurs. +func (c *Fake$.type|publicPlural$) Delete(name string, options *$.DeleteOptions|raw$) error { + _, err := c.Fake. + $if .namespaced$Invokes($.NewDeleteAction|raw$($.type|allLowercasePlural$Resource, c.ns, name), &$.type|raw${}) + $else$Invokes($.NewRootDeleteAction|raw$($.type|allLowercasePlural$Resource, name), &$.type|raw${})$end$ + return err +} +` + +var deleteCollectionTemplate = ` +// DeleteCollection deletes a collection of objects. +func (c *Fake$.type|publicPlural$) DeleteCollection(options *$.DeleteOptions|raw$, listOptions $.ListOptions|raw$) error { + $if .namespaced$action := $.NewDeleteCollectionAction|raw$($.type|allLowercasePlural$Resource, c.ns, listOptions) + $else$action := $.NewRootDeleteCollectionAction|raw$($.type|allLowercasePlural$Resource, listOptions) + $end$ + _, err := c.Fake.Invokes(action, &$.type|raw$List{}) + return err +} +` +var createTemplate = ` +// Create takes the representation of a $.inputType|private$ and creates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. +func (c *Fake$.type|publicPlural$) Create($.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { + obj, err := c.Fake. + $if .namespaced$Invokes($.NewCreateAction|raw$($.inputType|allLowercasePlural$Resource, c.ns, $.inputType|private$), &$.resultType|raw${}) + $else$Invokes($.NewRootCreateAction|raw$($.inputType|allLowercasePlural$Resource, $.inputType|private$), &$.resultType|raw${})$end$ + if obj == nil { + return nil, err + } + return obj.(*$.resultType|raw$), err +} +` + +var createSubresourceTemplate = ` +// Create takes the representation of a $.inputType|private$ and creates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. +func (c *Fake$.type|publicPlural$) Create($.type|private$Name string, $.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { + obj, err := c.Fake. + $if .namespaced$Invokes($.NewCreateSubresourceAction|raw$($.type|allLowercasePlural$Resource, $.type|private$Name, "$.subresourcePath$", c.ns, $.inputType|private$), &$.resultType|raw${}) + $else$Invokes($.NewRootCreateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", $.inputType|private$), &$.resultType|raw${})$end$ + if obj == nil { + return nil, err + } + return obj.(*$.resultType|raw$), err +} +` + +var updateTemplate = ` +// Update takes the representation of a $.inputType|private$ and updates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. +func (c *Fake$.type|publicPlural$) Update($.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { + obj, err := c.Fake. + $if .namespaced$Invokes($.NewUpdateAction|raw$($.inputType|allLowercasePlural$Resource, c.ns, $.inputType|private$), &$.resultType|raw${}) + $else$Invokes($.NewRootUpdateAction|raw$($.inputType|allLowercasePlural$Resource, $.inputType|private$), &$.resultType|raw${})$end$ + if obj == nil { + return nil, err + } + return obj.(*$.resultType|raw$), err +} +` + +var updateSubresourceTemplate = ` +// Update takes the representation of a $.inputType|private$ and updates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. +func (c *Fake$.type|publicPlural$) Update($.type|private$Name string, $.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { + obj, err := c.Fake. + $if .namespaced$Invokes($.NewUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", c.ns, $.inputType|private$), &$.inputType|raw${}) + $else$Invokes($.NewRootUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", $.inputType|private$), &$.resultType|raw${})$end$ + if obj == nil { + return nil, err + } + return obj.(*$.resultType|raw$), err +} +` + +var updateStatusTemplate = ` +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *Fake$.type|publicPlural$) UpdateStatus($.type|private$ *$.type|raw$) (*$.type|raw$, error) { + obj, err := c.Fake. + $if .namespaced$Invokes($.NewUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "status", c.ns, $.type|private$), &$.type|raw${}) + $else$Invokes($.NewRootUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "status", $.type|private$), &$.type|raw${})$end$ + if obj == nil { + return nil, err + } + return obj.(*$.type|raw$), err +} +` + +var watchTemplate = ` +// Watch returns a $.watchInterface|raw$ that watches the requested $.type|privatePlural$. +func (c *Fake$.type|publicPlural$) Watch(opts $.ListOptions|raw$) ($.watchInterface|raw$, error) { + return c.Fake. + $if .namespaced$InvokesWatch($.NewWatchAction|raw$($.type|allLowercasePlural$Resource, c.ns, opts)) + $else$InvokesWatch($.NewRootWatchAction|raw$($.type|allLowercasePlural$Resource, opts))$end$ +} +` + +var patchTemplate = ` +// Patch applies the patch and returns the patched $.resultType|private$. +func (c *Fake$.type|publicPlural$) Patch(name string, pt $.PatchType|raw$, data []byte, subresources ...string) (result *$.resultType|raw$, err error) { + obj, err := c.Fake. + $if .namespaced$Invokes($.NewPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, c.ns, name, data, subresources... ), &$.resultType|raw${}) + $else$Invokes($.NewRootPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, name, data, subresources...), &$.resultType|raw${})$end$ + if obj == nil { + return nil, err + } + return obj.(*$.resultType|raw$), err +} +` diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_clientset.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_clientset.go new file mode 100644 index 0000000000..18ec09ac6f --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_clientset.go @@ -0,0 +1,192 @@ +/* +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 generators + +import ( + "fmt" + "io" + "path/filepath" + "strings" + + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" +) + +// genClientset generates a package for a clientset. +type genClientset struct { + generator.DefaultGen + groups []clientgentypes.GroupVersions + groupGoNames map[clientgentypes.GroupVersion]string + clientsetPackage string + outputPackage string + imports namer.ImportTracker + clientsetGenerated bool +} + +var _ generator.Generator = &genClientset{} + +func (g *genClientset) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +// We only want to call GenerateType() once. +func (g *genClientset) Filter(c *generator.Context, t *types.Type) bool { + ret := !g.clientsetGenerated + g.clientsetGenerated = true + return ret +} + +func (g *genClientset) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.imports.ImportLines()...) + for _, group := range g.groups { + for _, version := range group.Versions { + typedClientPath := filepath.Join(g.clientsetPackage, "typed", group.PackageName, version.NonEmpty()) + groupAlias := strings.ToLower(g.groupGoNames[clientgentypes.GroupVersion{Group: group.Group, Version: version.Version}]) + imports = append(imports, strings.ToLower(fmt.Sprintf("%s%s \"%s\"", groupAlias, version.NonEmpty(), typedClientPath))) + } + } + return +} + +func (g *genClientset) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + // TODO: We actually don't need any type information to generate the clientset, + // perhaps we can adapt the go2ild framework to this kind of usage. + sw := generator.NewSnippetWriter(w, c, "$", "$") + + allGroups := clientgentypes.ToGroupVersionInfo(g.groups, g.groupGoNames) + m := map[string]interface{}{ + "allGroups": allGroups, + "Config": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Config"}), + "DefaultKubernetesUserAgent": c.Universe.Function(types.Name{Package: "k8s.io/client-go/rest", Name: "DefaultKubernetesUserAgent"}), + "RESTClientInterface": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Interface"}), + "DiscoveryInterface": c.Universe.Type(types.Name{Package: "k8s.io/client-go/discovery", Name: "DiscoveryInterface"}), + "DiscoveryClient": c.Universe.Type(types.Name{Package: "k8s.io/client-go/discovery", Name: "DiscoveryClient"}), + "NewDiscoveryClientForConfig": c.Universe.Function(types.Name{Package: "k8s.io/client-go/discovery", Name: "NewDiscoveryClientForConfig"}), + "NewDiscoveryClientForConfigOrDie": c.Universe.Function(types.Name{Package: "k8s.io/client-go/discovery", Name: "NewDiscoveryClientForConfigOrDie"}), + "NewDiscoveryClient": c.Universe.Function(types.Name{Package: "k8s.io/client-go/discovery", Name: "NewDiscoveryClient"}), + "flowcontrolNewTokenBucketRateLimiter": c.Universe.Function(types.Name{Package: "k8s.io/client-go/util/flowcontrol", Name: "NewTokenBucketRateLimiter"}), + } + sw.Do(clientsetInterface, m) + sw.Do(clientsetTemplate, m) + for _, g := range allGroups { + sw.Do(clientsetInterfaceImplTemplate, g) + // don't generated the default method if generating internalversion clientset + if g.IsDefaultVersion && g.Version != "" { + sw.Do(clientsetInterfaceDefaultVersionImpl, g) + } + } + sw.Do(getDiscoveryTemplate, m) + sw.Do(newClientsetForConfigTemplate, m) + sw.Do(newClientsetForConfigOrDieTemplate, m) + sw.Do(newClientsetForRESTClientTemplate, m) + + return sw.Error() +} + +var clientsetInterface = ` +type Interface interface { + Discovery() $.DiscoveryInterface|raw$ + $range .allGroups$$.GroupGoName$$.Version$() $.PackageAlias$.$.GroupGoName$$.Version$Interface + $if .IsDefaultVersion$// Deprecated: please explicitly pick a version if possible. + $.GroupGoName$() $.PackageAlias$.$.GroupGoName$$.Version$Interface + $end$$end$ +} +` + +var clientsetTemplate = ` +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *$.DiscoveryClient|raw$ + $range .allGroups$$.LowerCaseGroupGoName$$.Version$ *$.PackageAlias$.$.GroupGoName$$.Version$Client + $end$ +} +` + +var clientsetInterfaceImplTemplate = ` +// $.GroupGoName$$.Version$ retrieves the $.GroupGoName$$.Version$Client +func (c *Clientset) $.GroupGoName$$.Version$() $.PackageAlias$.$.GroupGoName$$.Version$Interface { + return c.$.LowerCaseGroupGoName$$.Version$ +} +` + +var clientsetInterfaceDefaultVersionImpl = ` +// Deprecated: $.GroupGoName$ retrieves the default version of $.GroupGoName$Client. +// Please explicitly pick a version. +func (c *Clientset) $.GroupGoName$() $.PackageAlias$.$.GroupGoName$$.Version$Interface { + return c.$.LowerCaseGroupGoName$$.Version$ +} +` + +var getDiscoveryTemplate = ` +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() $.DiscoveryInterface|raw$ { + if c == nil { + return nil + } + return c.DiscoveryClient +} +` + +var newClientsetForConfigTemplate = ` +// NewForConfig creates a new Clientset for the given config. +func NewForConfig(c *$.Config|raw$) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + configShallowCopy.RateLimiter = $.flowcontrolNewTokenBucketRateLimiter|raw$(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error +$range .allGroups$ cs.$.LowerCaseGroupGoName$$.Version$, err =$.PackageAlias$.NewForConfig(&configShallowCopy) + if err!=nil { + return nil, err + } +$end$ + cs.DiscoveryClient, err = $.NewDiscoveryClientForConfig|raw$(&configShallowCopy) + if err!=nil { + return nil, err + } + return &cs, nil +} +` + +var newClientsetForConfigOrDieTemplate = ` +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *$.Config|raw$) *Clientset { + var cs Clientset +$range .allGroups$ cs.$.LowerCaseGroupGoName$$.Version$ =$.PackageAlias$.NewForConfigOrDie(c) +$end$ + cs.DiscoveryClient = $.NewDiscoveryClientForConfigOrDie|raw$(c) + return &cs +} +` + +var newClientsetForRESTClientTemplate = ` +// New creates a new Clientset for the given RESTClient. +func New(c $.RESTClientInterface|raw$) *Clientset { + var cs Clientset +$range .allGroups$ cs.$.LowerCaseGroupGoName$$.Version$ =$.PackageAlias$.New(c) +$end$ + cs.DiscoveryClient = $.NewDiscoveryClient|raw$(c) + return &cs +} +` diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_expansion.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_expansion.go new file mode 100644 index 0000000000..f47c079e02 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_expansion.go @@ -0,0 +1,54 @@ +/* +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 generators + +import ( + "io" + "os" + "path/filepath" + "strings" + + "k8s.io/gengo/generator" + "k8s.io/gengo/types" +) + +// genExpansion produces a file for a group client, e.g. ExtensionsClient for the extension group. +type genExpansion struct { + generator.DefaultGen + groupPackagePath string + // types in a group + types []*types.Type +} + +// We only want to call GenerateType() once per group. +func (g *genExpansion) Filter(c *generator.Context, t *types.Type) bool { + return len(g.types) == 0 || t == g.types[0] +} + +func (g *genExpansion) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + for _, t := range g.types { + if _, err := os.Stat(filepath.Join(g.groupPackagePath, strings.ToLower(t.Name.Name+"_expansion.go"))); os.IsNotExist(err) { + sw.Do(expansionInterfaceTemplate, t) + } + } + return sw.Error() +} + +var expansionInterfaceTemplate = ` +type $.|public$Expansion interface {} +` diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_group.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_group.go new file mode 100644 index 0000000000..fd59715c42 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_group.go @@ -0,0 +1,247 @@ +/* +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 generators + +import ( + "io" + "path/filepath" + + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/code-generator/cmd/client-gen/generators/util" + "k8s.io/code-generator/cmd/client-gen/path" +) + +// genGroup produces a file for a group client, e.g. ExtensionsClient for the extension group. +type genGroup struct { + generator.DefaultGen + outputPackage string + group string + version string + groupGoName string + apiPath string + // types in this group + types []*types.Type + imports namer.ImportTracker + inputPackage string + clientsetPackage string + // If the genGroup has been called. This generator should only execute once. + called bool +} + +var _ generator.Generator = &genGroup{} + +// We only want to call GenerateType() once per group. +func (g *genGroup) Filter(c *generator.Context, t *types.Type) bool { + if !g.called { + g.called = true + return true + } + return false +} + +func (g *genGroup) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *genGroup) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.imports.ImportLines()...) + imports = append(imports, filepath.Join(g.clientsetPackage, "scheme")) + return +} + +func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + + apiPath := func(group string) string { + if group == "core" { + return `"/api"` + } + return `"` + g.apiPath + `"` + } + + groupName := g.group + if g.group == "core" { + groupName = "" + } + // allow user to define a group name that's different from the one parsed from the directory. + p := c.Universe.Package(path.Vendorless(g.inputPackage)) + if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil { + groupName = override[0] + } + + m := map[string]interface{}{ + "group": g.group, + "version": g.version, + "groupName": groupName, + "GroupGoName": g.groupGoName, + "Version": namer.IC(g.version), + "types": g.types, + "apiPath": apiPath(g.group), + "schemaGroupVersion": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime/schema", Name: "GroupVersion"}), + "runtimeAPIVersionInternal": c.Universe.Variable(types.Name{Package: "k8s.io/apimachinery/pkg/runtime", Name: "APIVersionInternal"}), + "serializerDirectCodecFactory": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime/serializer", Name: "DirectCodecFactory"}), + "restConfig": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Config"}), + "restDefaultKubernetesUserAgent": c.Universe.Function(types.Name{Package: "k8s.io/client-go/rest", Name: "DefaultKubernetesUserAgent"}), + "restRESTClientInterface": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Interface"}), + "restRESTClientFor": c.Universe.Function(types.Name{Package: "k8s.io/client-go/rest", Name: "RESTClientFor"}), + "SchemeGroupVersion": c.Universe.Variable(types.Name{Package: path.Vendorless(g.inputPackage), Name: "SchemeGroupVersion"}), + } + sw.Do(groupInterfaceTemplate, m) + sw.Do(groupClientTemplate, m) + for _, t := range g.types { + tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + if err != nil { + return err + } + wrapper := map[string]interface{}{ + "type": t, + "GroupGoName": g.groupGoName, + "Version": namer.IC(g.version), + } + if tags.NonNamespaced { + sw.Do(getterImplNonNamespaced, wrapper) + } else { + sw.Do(getterImplNamespaced, wrapper) + } + } + sw.Do(newClientForConfigTemplate, m) + sw.Do(newClientForConfigOrDieTemplate, m) + sw.Do(newClientForRESTClientTemplate, m) + if g.version == "" { + sw.Do(setInternalVersionClientDefaultsTemplate, m) + } else { + sw.Do(setClientDefaultsTemplate, m) + } + sw.Do(getRESTClient, m) + + return sw.Error() +} + +var groupInterfaceTemplate = ` +type $.GroupGoName$$.Version$Interface interface { + RESTClient() $.restRESTClientInterface|raw$ + $range .types$ $.|publicPlural$Getter + $end$ +} +` + +var groupClientTemplate = ` +// $.GroupGoName$$.Version$Client is used to interact with features provided by the $.groupName$ group. +type $.GroupGoName$$.Version$Client struct { + restClient $.restRESTClientInterface|raw$ +} +` + +var getterImplNamespaced = ` +func (c *$.GroupGoName$$.Version$Client) $.type|publicPlural$(namespace string) $.type|public$Interface { + return new$.type|publicPlural$(c, namespace) +} +` + +var getterImplNonNamespaced = ` +func (c *$.GroupGoName$$.Version$Client) $.type|publicPlural$() $.type|public$Interface { + return new$.type|publicPlural$(c) +} +` + +var newClientForConfigTemplate = ` +// NewForConfig creates a new $.GroupGoName$$.Version$Client for the given config. +func NewForConfig(c *$.restConfig|raw$) (*$.GroupGoName$$.Version$Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := $.restRESTClientFor|raw$(&config) + if err != nil { + return nil, err + } + return &$.GroupGoName$$.Version$Client{client}, nil +} +` + +var newClientForConfigOrDieTemplate = ` +// NewForConfigOrDie creates a new $.GroupGoName$$.Version$Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *$.restConfig|raw$) *$.GroupGoName$$.Version$Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} +` + +var getRESTClient = ` +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *$.GroupGoName$$.Version$Client) RESTClient() $.restRESTClientInterface|raw$ { + if c == nil { + return nil + } + return c.restClient +} +` + +var newClientForRESTClientTemplate = ` +// New creates a new $.GroupGoName$$.Version$Client for the given RESTClient. +func New(c $.restRESTClientInterface|raw$) *$.GroupGoName$$.Version$Client { + return &$.GroupGoName$$.Version$Client{c} +} +` + +var setInternalVersionClientDefaultsTemplate = ` +func setConfigDefaults(config *$.restConfig|raw$) error { + config.APIPath = $.apiPath$ + if config.UserAgent == "" { + config.UserAgent = $.restDefaultKubernetesUserAgent|raw$() + } + if config.GroupVersion == nil || config.GroupVersion.Group != scheme.Scheme.PrioritizedVersionsForGroup("$.groupName$")[0].Group { + gv := scheme.Scheme.PrioritizedVersionsForGroup("$.groupName$")[0] + config.GroupVersion = &gv + } + config.NegotiatedSerializer = scheme.Codecs + + if config.QPS == 0 { + config.QPS = 5 + } + if config.Burst == 0 { + config.Burst = 10 + } + + return nil +} +` + +var setClientDefaultsTemplate = ` +func setConfigDefaults(config *$.restConfig|raw$) error { + gv := $.SchemeGroupVersion|raw$ + config.GroupVersion = &gv + config.APIPath = $.apiPath$ + config.NegotiatedSerializer = $.serializerDirectCodecFactory|raw${CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = $.restDefaultKubernetesUserAgent|raw$() + } + + return nil +} +` diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_type.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_type.go new file mode 100644 index 0000000000..92e2a97f1d --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/generator_for_type.go @@ -0,0 +1,579 @@ +/* +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 generators + +import ( + "io" + "path/filepath" + "strings" + + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/code-generator/cmd/client-gen/generators/util" +) + +// genClientForType produces a file for each top-level type. +type genClientForType struct { + generator.DefaultGen + outputPackage string + clientsetPackage string + group string + version string + groupGoName string + typeToMatch *types.Type + imports namer.ImportTracker +} + +var _ generator.Generator = &genClientForType{} + +// Filter ignores all but one type because we're making a single file per type. +func (g *genClientForType) Filter(c *generator.Context, t *types.Type) bool { return t == g.typeToMatch } + +func (g *genClientForType) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *genClientForType) Imports(c *generator.Context) (imports []string) { + return g.imports.ImportLines() +} + +// Ideally, we'd like genStatus to return true if there is a subresource path +// registered for "status" in the API server, but we do not have that +// information, so genStatus returns true if the type has a status field. +func genStatus(t *types.Type) bool { + // Default to true if we have a Status member + hasStatus := false + for _, m := range t.Members { + if m.Name == "Status" { + hasStatus = true + break + } + } + return hasStatus && !util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)).NoStatus +} + +// GenerateType makes the body of a file implementing the individual typed client for type t. +func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + pkg := filepath.Base(t.Name.Package) + tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + if err != nil { + return err + } + type extendedInterfaceMethod struct { + template string + args map[string]interface{} + } + extendedMethods := []extendedInterfaceMethod{} + for _, e := range tags.Extensions { + inputType := *t + resultType := *t + // TODO: Extract this to some helper method as this code is copied into + // 2 other places. + if len(e.InputTypeOverride) > 0 { + if name, pkg := e.Input(); len(pkg) > 0 { + newType := c.Universe.Type(types.Name{Package: pkg, Name: name}) + inputType = *newType + } else { + inputType.Name.Name = e.InputTypeOverride + } + } + if len(e.ResultTypeOverride) > 0 { + if name, pkg := e.Result(); len(pkg) > 0 { + newType := c.Universe.Type(types.Name{Package: pkg, Name: name}) + resultType = *newType + } else { + resultType.Name.Name = e.ResultTypeOverride + } + } + var updatedVerbtemplate string + if _, exists := subresourceDefaultVerbTemplates[e.VerbType]; e.IsSubresource() && exists { + updatedVerbtemplate = e.VerbName + "(" + strings.TrimPrefix(subresourceDefaultVerbTemplates[e.VerbType], strings.Title(e.VerbType)+"(") + } else { + updatedVerbtemplate = e.VerbName + "(" + strings.TrimPrefix(defaultVerbTemplates[e.VerbType], strings.Title(e.VerbType)+"(") + } + extendedMethods = append(extendedMethods, extendedInterfaceMethod{ + template: updatedVerbtemplate, + args: map[string]interface{}{ + "type": t, + "inputType": &inputType, + "resultType": &resultType, + "DeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "DeleteOptions"}), + "ListOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ListOptions"}), + "GetOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "GetOptions"}), + "PatchType": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/types", Name: "PatchType"}), + }, + }) + } + m := map[string]interface{}{ + "type": t, + "inputType": t, + "resultType": t, + "package": pkg, + "Package": namer.IC(pkg), + "namespaced": !tags.NonNamespaced, + "Group": namer.IC(g.group), + "subresource": false, + "subresourcePath": "", + "GroupGoName": g.groupGoName, + "Version": namer.IC(g.version), + "DeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "DeleteOptions"}), + "ListOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ListOptions"}), + "GetOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "GetOptions"}), + "PatchType": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/types", Name: "PatchType"}), + "watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/watch", Name: "Interface"}), + "RESTClientInterface": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Interface"}), + "schemeParameterCodec": c.Universe.Variable(types.Name{Package: filepath.Join(g.clientsetPackage, "scheme"), Name: "ParameterCodec"}), + } + + sw.Do(getterComment, m) + if tags.NonNamespaced { + sw.Do(getterNonNamespaced, m) + } else { + sw.Do(getterNamespaced, m) + } + + sw.Do(interfaceTemplate1, m) + if !tags.NoVerbs { + if !genStatus(t) { + tags.SkipVerbs = append(tags.SkipVerbs, "updateStatus") + } + interfaceSuffix := "" + if len(extendedMethods) > 0 { + interfaceSuffix = "\n" + } + sw.Do("\n"+generateInterface(tags)+interfaceSuffix, m) + // add extended verbs into interface + for _, v := range extendedMethods { + sw.Do(v.template+interfaceSuffix, v.args) + } + + } + sw.Do(interfaceTemplate4, m) + + if tags.NonNamespaced { + sw.Do(structNonNamespaced, m) + sw.Do(newStructNonNamespaced, m) + } else { + sw.Do(structNamespaced, m) + sw.Do(newStructNamespaced, m) + } + + if tags.NoVerbs { + return sw.Error() + } + + if tags.HasVerb("get") { + sw.Do(getTemplate, m) + } + if tags.HasVerb("list") { + sw.Do(listTemplate, m) + } + if tags.HasVerb("watch") { + sw.Do(watchTemplate, m) + } + + if tags.HasVerb("create") { + sw.Do(createTemplate, m) + } + if tags.HasVerb("update") { + sw.Do(updateTemplate, m) + } + if tags.HasVerb("updateStatus") { + sw.Do(updateStatusTemplate, m) + } + if tags.HasVerb("delete") { + sw.Do(deleteTemplate, m) + } + if tags.HasVerb("deleteCollection") { + sw.Do(deleteCollectionTemplate, m) + } + if tags.HasVerb("patch") { + sw.Do(patchTemplate, m) + } + + // generate expansion methods + for _, e := range tags.Extensions { + inputType := *t + resultType := *t + if len(e.InputTypeOverride) > 0 { + if name, pkg := e.Input(); len(pkg) > 0 { + newType := c.Universe.Type(types.Name{Package: pkg, Name: name}) + inputType = *newType + } else { + inputType.Name.Name = e.InputTypeOverride + } + } + if len(e.ResultTypeOverride) > 0 { + if name, pkg := e.Result(); len(pkg) > 0 { + newType := c.Universe.Type(types.Name{Package: pkg, Name: name}) + resultType = *newType + } else { + resultType.Name.Name = e.ResultTypeOverride + } + } + m["inputType"] = &inputType + m["resultType"] = &resultType + m["subresourcePath"] = e.SubResourcePath + + if e.HasVerb("get") { + if e.IsSubresource() { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, getSubresourceTemplate), m) + } else { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, getTemplate), m) + } + } + + if e.HasVerb("list") { + if e.IsSubresource() { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, listSubresourceTemplate), m) + } else { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, listTemplate), m) + } + } + + // TODO: Figure out schemantic for watching a sub-resource. + if e.HasVerb("watch") { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, watchTemplate), m) + } + + if e.HasVerb("create") { + if e.IsSubresource() { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, createSubresourceTemplate), m) + } else { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, createTemplate), m) + } + } + + if e.HasVerb("update") { + if e.IsSubresource() { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, updateSubresourceTemplate), m) + } else { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, updateTemplate), m) + } + } + + // TODO: Figure out schemantic for deleting a sub-resource (what arguments + // are passed, does it need two names? etc. + if e.HasVerb("delete") { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, deleteTemplate), m) + } + + if e.HasVerb("patch") { + sw.Do(adjustTemplate(e.VerbName, e.VerbType, patchTemplate), m) + } + } + + return sw.Error() +} + +// adjustTemplate adjust the origin verb template using the expansion name. +// TODO: Make the verbs in templates parametrized so the strings.Replace() is +// not needed. +func adjustTemplate(name, verbType, template string) string { + return strings.Replace(template, " "+strings.Title(verbType), " "+name, -1) +} + +func generateInterface(tags util.Tags) string { + // need an ordered list here to guarantee order of generated methods. + out := []string{} + for _, m := range util.SupportedVerbs { + if tags.HasVerb(m) { + out = append(out, defaultVerbTemplates[m]) + } + } + return strings.Join(out, "\n") +} + +var subresourceDefaultVerbTemplates = map[string]string{ + "create": `Create($.type|private$Name string, $.inputType|private$ *$.inputType|raw$) (*$.resultType|raw$, error)`, + "list": `List($.type|private$Name string, opts $.ListOptions|raw$) (*$.resultType|raw$List, error)`, + "update": `Update($.type|private$Name string, $.inputType|private$ *$.inputType|raw$) (*$.resultType|raw$, error)`, + "get": `Get($.type|private$Name string, options $.GetOptions|raw$) (*$.resultType|raw$, error)`, +} + +var defaultVerbTemplates = map[string]string{ + "create": `Create(*$.inputType|raw$) (*$.resultType|raw$, error)`, + "update": `Update(*$.inputType|raw$) (*$.resultType|raw$, error)`, + "updateStatus": `UpdateStatus(*$.type|raw$) (*$.type|raw$, error)`, + "delete": `Delete(name string, options *$.DeleteOptions|raw$) error`, + "deleteCollection": `DeleteCollection(options *$.DeleteOptions|raw$, listOptions $.ListOptions|raw$) error`, + "get": `Get(name string, options $.GetOptions|raw$) (*$.resultType|raw$, error)`, + "list": `List(opts $.ListOptions|raw$) (*$.resultType|raw$List, error)`, + "watch": `Watch(opts $.ListOptions|raw$) ($.watchInterface|raw$, error)`, + "patch": `Patch(name string, pt $.PatchType|raw$, data []byte, subresources ...string) (result *$.resultType|raw$, err error)`, +} + +// group client will implement this interface. +var getterComment = ` +// $.type|publicPlural$Getter has a method to return a $.type|public$Interface. +// A group's client should implement this interface.` + +var getterNamespaced = ` +type $.type|publicPlural$Getter interface { + $.type|publicPlural$(namespace string) $.type|public$Interface +} +` + +var getterNonNamespaced = ` +type $.type|publicPlural$Getter interface { + $.type|publicPlural$() $.type|public$Interface +} +` + +// this type's interface, typed client will implement this interface. +var interfaceTemplate1 = ` +// $.type|public$Interface has methods to work with $.type|public$ resources. +type $.type|public$Interface interface {` + +var interfaceTemplate4 = ` + $.type|public$Expansion +} +` + +// template for the struct that implements the type's interface +var structNamespaced = ` +// $.type|privatePlural$ implements $.type|public$Interface +type $.type|privatePlural$ struct { + client $.RESTClientInterface|raw$ + ns string +} +` + +// template for the struct that implements the type's interface +var structNonNamespaced = ` +// $.type|privatePlural$ implements $.type|public$Interface +type $.type|privatePlural$ struct { + client $.RESTClientInterface|raw$ +} +` + +var newStructNamespaced = ` +// new$.type|publicPlural$ returns a $.type|publicPlural$ +func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client, namespace string) *$.type|privatePlural$ { + return &$.type|privatePlural${ + client: c.RESTClient(), + ns: namespace, + } +} +` + +var newStructNonNamespaced = ` +// new$.type|publicPlural$ returns a $.type|publicPlural$ +func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client) *$.type|privatePlural$ { + return &$.type|privatePlural${ + client: c.RESTClient(), + } +} +` +var listTemplate = ` +// List takes label and field selectors, and returns the list of $.resultType|publicPlural$ that match those selectors. +func (c *$.type|privatePlural$) List(opts $.ListOptions|raw$) (result *$.resultType|raw$List, err error) { + result = &$.resultType|raw$List{} + err = c.client.Get(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + VersionedParams(&opts, $.schemeParameterCodec|raw$). + Do(). + Into(result) + return +} +` + +var listSubresourceTemplate = ` +// List takes $.type|raw$ name, label and field selectors, and returns the list of $.resultType|publicPlural$ that match those selectors. +func (c *$.type|privatePlural$) List($.type|private$Name string, opts $.ListOptions|raw$) (result *$.resultType|raw$List, err error) { + result = &$.resultType|raw$List{} + err = c.client.Get(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + Name($.type|private$Name). + SubResource("$.subresourcePath$"). + VersionedParams(&opts, $.schemeParameterCodec|raw$). + Do(). + Into(result) + return +} +` + +var getTemplate = ` +// Get takes name of the $.type|private$, and returns the corresponding $.resultType|private$ object, and an error if there is any. +func (c *$.type|privatePlural$) Get(name string, options $.GetOptions|raw$) (result *$.resultType|raw$, err error) { + result = &$.resultType|raw${} + err = c.client.Get(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + Name(name). + VersionedParams(&options, $.schemeParameterCodec|raw$). + Do(). + Into(result) + return +} +` + +var getSubresourceTemplate = ` +// Get takes name of the $.type|private$, and returns the corresponding $.resultType|raw$ object, and an error if there is any. +func (c *$.type|privatePlural$) Get($.type|private$Name string, options $.GetOptions|raw$) (result *$.resultType|raw$, err error) { + result = &$.resultType|raw${} + err = c.client.Get(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + Name($.type|private$Name). + SubResource("$.subresourcePath$"). + VersionedParams(&options, $.schemeParameterCodec|raw$). + Do(). + Into(result) + return +} +` + +var deleteTemplate = ` +// Delete takes name of the $.type|private$ and deletes it. Returns an error if one occurs. +func (c *$.type|privatePlural$) Delete(name string, options *$.DeleteOptions|raw$) error { + return c.client.Delete(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + Name(name). + Body(options). + Do(). + Error() +} +` + +var deleteCollectionTemplate = ` +// DeleteCollection deletes a collection of objects. +func (c *$.type|privatePlural$) DeleteCollection(options *$.DeleteOptions|raw$, listOptions $.ListOptions|raw$) error { + return c.client.Delete(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + VersionedParams(&listOptions, $.schemeParameterCodec|raw$). + Body(options). + Do(). + Error() +} +` + +var createSubresourceTemplate = ` +// Create takes the representation of a $.inputType|private$ and creates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. +func (c *$.type|privatePlural$) Create($.type|private$Name string, $.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { + result = &$.resultType|raw${} + err = c.client.Post(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + Name($.type|private$Name). + SubResource("$.subresourcePath$"). + Body($.inputType|private$). + Do(). + Into(result) + return +} +` + +var createTemplate = ` +// Create takes the representation of a $.inputType|private$ and creates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. +func (c *$.type|privatePlural$) Create($.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { + result = &$.resultType|raw${} + err = c.client.Post(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + Body($.inputType|private$). + Do(). + Into(result) + return +} +` + +var updateSubresourceTemplate = ` +// Update takes the top resource name and the representation of a $.inputType|private$ and updates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. +func (c *$.type|privatePlural$) Update($.type|private$Name string, $.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { + result = &$.resultType|raw${} + err = c.client.Put(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + Name($.type|private$Name). + SubResource("$.subresourcePath$"). + Body($.inputType|private$). + Do(). + Into(result) + return +} +` + +var updateTemplate = ` +// Update takes the representation of a $.inputType|private$ and updates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. +func (c *$.type|privatePlural$) Update($.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { + result = &$.resultType|raw${} + err = c.client.Put(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + Name($.inputType|private$.Name). + Body($.inputType|private$). + Do(). + Into(result) + return +} +` + +var updateStatusTemplate = ` +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *$.type|privatePlural$) UpdateStatus($.type|private$ *$.type|raw$) (result *$.type|raw$, err error) { + result = &$.type|raw${} + err = c.client.Put(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + Name($.type|private$.Name). + SubResource("status"). + Body($.type|private$). + Do(). + Into(result) + return +} +` + +var watchTemplate = ` +// Watch returns a $.watchInterface|raw$ that watches the requested $.type|privatePlural$. +func (c *$.type|privatePlural$) Watch(opts $.ListOptions|raw$) ($.watchInterface|raw$, error) { + opts.Watch = true + return c.client.Get(). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + VersionedParams(&opts, $.schemeParameterCodec|raw$). + Watch() +} +` + +var patchTemplate = ` +// Patch applies the patch and returns the patched $.resultType|private$. +func (c *$.type|privatePlural$) Patch(name string, pt $.PatchType|raw$, data []byte, subresources ...string) (result *$.resultType|raw$, err error) { + result = &$.resultType|raw${} + err = c.client.Patch(pt). + $if .namespaced$Namespace(c.ns).$end$ + Resource("$.type|resource$"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} +` diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/scheme/generator_for_scheme.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/scheme/generator_for_scheme.go new file mode 100644 index 0000000000..60cfbcc0f5 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/scheme/generator_for_scheme.go @@ -0,0 +1,185 @@ +/* +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 scheme + +import ( + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "k8s.io/code-generator/cmd/client-gen/path" + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" +) + +// GenScheme produces a package for a clientset with the scheme, codecs and parameter codecs. +type GenScheme struct { + generator.DefaultGen + OutputPackage string + Groups []clientgentypes.GroupVersions + GroupGoNames map[clientgentypes.GroupVersion]string + InputPackages map[clientgentypes.GroupVersion]string + OutputPath string + ImportTracker namer.ImportTracker + PrivateScheme bool + CreateRegistry bool + schemeGenerated bool +} + +func (g *GenScheme) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.OutputPackage, g.ImportTracker), + } +} + +// We only want to call GenerateType() once. +func (g *GenScheme) Filter(c *generator.Context, t *types.Type) bool { + ret := !g.schemeGenerated + g.schemeGenerated = true + return ret +} + +func (g *GenScheme) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.ImportTracker.ImportLines()...) + for _, group := range g.Groups { + for _, version := range group.Versions { + packagePath := g.InputPackages[clientgentypes.GroupVersion{Group: group.Group, Version: version.Version}] + groupAlias := strings.ToLower(g.GroupGoNames[clientgentypes.GroupVersion{Group: group.Group, Version: version.Version}]) + if g.CreateRegistry { + // import the install package for internal clientsets instead of the type package with register.go + if version.Version != "" { + packagePath = filepath.Dir(packagePath) + } + packagePath = filepath.Join(packagePath, "install") + imports = append(imports, strings.ToLower(fmt.Sprintf("%s \"%s\"", groupAlias, path.Vendorless(packagePath)))) + break + } else { + imports = append(imports, strings.ToLower(fmt.Sprintf("%s%s \"%s\"", groupAlias, version.Version.NonEmpty(), path.Vendorless(packagePath)))) + } + } + } + return +} + +func (g *GenScheme) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + + allGroupVersions := clientgentypes.ToGroupVersionInfo(g.Groups, g.GroupGoNames) + allInstallGroups := clientgentypes.ToGroupInstallPackages(g.Groups, g.GroupGoNames) + + m := map[string]interface{}{ + "allGroupVersions": allGroupVersions, + "allInstallGroups": allInstallGroups, + "customRegister": false, + "runtimeNewParameterCodec": c.Universe.Function(types.Name{Package: "k8s.io/apimachinery/pkg/runtime", Name: "NewParameterCodec"}), + "runtimeNewScheme": c.Universe.Function(types.Name{Package: "k8s.io/apimachinery/pkg/runtime", Name: "NewScheme"}), + "serializerNewCodecFactory": c.Universe.Function(types.Name{Package: "k8s.io/apimachinery/pkg/runtime/serializer", Name: "NewCodecFactory"}), + "runtimeScheme": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime", Name: "Scheme"}), + "runtimeSchemeBuilder": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime", Name: "SchemeBuilder"}), + "runtimeUtilMust": c.Universe.Function(types.Name{Package: "k8s.io/apimachinery/pkg/util/runtime", Name: "Must"}), + "schemaGroupVersion": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime/schema", Name: "GroupVersion"}), + "metav1AddToGroupVersion": c.Universe.Function(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "AddToGroupVersion"}), + } + globals := map[string]string{ + "Scheme": "Scheme", + "Codecs": "Codecs", + "ParameterCodec": "ParameterCodec", + "Registry": "Registry", + } + for k, v := range globals { + if g.PrivateScheme { + m[k] = strings.ToLower(v[0:1]) + v[1:] + } else { + m[k] = v + } + } + + sw.Do(globalsTemplate, m) + + if g.OutputPath != "" { + if _, err := os.Stat(filepath.Join(g.OutputPath, strings.ToLower("register_custom.go"))); err == nil { + m["customRegister"] = true + } + } + + if g.CreateRegistry { + sw.Do(registryRegistration, m) + } else { + sw.Do(simpleRegistration, m) + } + + return sw.Error() +} + +var globalsTemplate = ` +var $.Scheme$ = $.runtimeNewScheme|raw$() +var $.Codecs$ = $.serializerNewCodecFactory|raw$($.Scheme$) +var $.ParameterCodec$ = $.runtimeNewParameterCodec|raw$($.Scheme$)` + +var registryRegistration = ` + +func init() { + $.metav1AddToGroupVersion|raw$($.Scheme$, $.schemaGroupVersion|raw${Version: "v1"}) + Install($.Scheme$) +} + +// Install registers the API group and adds types to a scheme +func Install(scheme *$.runtimeScheme|raw$) { + $- range .allInstallGroups$ + $.InstallPackageAlias$.Install(scheme) + $- end$ + $if .customRegister$ + ExtraInstall(scheme) + $end -$ +} +` + +var simpleRegistration = ` +var localSchemeBuilder = $.runtimeSchemeBuilder|raw${ + $- range .allGroupVersions$ + $.PackageAlias$.AddToScheme, + $- end$ + $if .customRegister$ + ExtraAddToScheme, + $end -$ +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + $.metav1AddToGroupVersion|raw$($.Scheme$, $.schemaGroupVersion|raw${Version: "v1"}) + $.runtimeUtilMust|raw$(AddToScheme($.Scheme$)) +} +` diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/tags.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/tags.go new file mode 100644 index 0000000000..b004081036 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/tags.go @@ -0,0 +1,32 @@ +/* +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 generators + +import ( + "k8s.io/gengo/types" +) + +// extractTag gets the comment-tags for the key. If the tag did not exist, it +// returns the empty string. +func extractTag(key string, lines []string) string { + val, present := types.ExtractCommentTags("+", lines)[key] + if !present || len(val) < 1 { + return "" + } + + return val[0] +} diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/generators/util/tags.go b/vendor/k8s.io/code-generator/cmd/client-gen/generators/util/tags.go new file mode 100644 index 0000000000..0b7d68ca88 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/generators/util/tags.go @@ -0,0 +1,341 @@ +/* +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 util + +import ( + "errors" + "fmt" + "strings" + + "k8s.io/gengo/types" +) + +var supportedTags = []string{ + "genclient", + "genclient:nonNamespaced", + "genclient:noVerbs", + "genclient:onlyVerbs", + "genclient:skipVerbs", + "genclient:noStatus", + "genclient:readonly", + "genclient:method", +} + +// SupportedVerbs is a list of supported verbs for +onlyVerbs and +skipVerbs. +var SupportedVerbs = []string{ + "create", + "update", + "updateStatus", + "delete", + "deleteCollection", + "get", + "list", + "watch", + "patch", +} + +// ReadonlyVerbs represents a list of read-only verbs. +var ReadonlyVerbs = []string{ + "get", + "list", + "watch", +} + +// genClientPrefix is the default prefix for all genclient tags. +const genClientPrefix = "genclient:" + +// unsupportedExtensionVerbs is a list of verbs we don't support generating +// extension client functions for. +var unsupportedExtensionVerbs = []string{ + "updateStatus", + "deleteCollection", + "watch", + "delete", +} + +// inputTypeSupportedVerbs is a list of verb types that supports overriding the +// input argument type. +var inputTypeSupportedVerbs = []string{ + "create", + "update", +} + +// resultTypeSupportedVerbs is a list of verb types that supports overriding the +// resulting type. +var resultTypeSupportedVerbs = []string{ + "create", + "update", + "get", + "list", + "patch", +} + +// Extensions allows to extend the default set of client verbs +// (CRUD+watch+patch+list+deleteCollection) for a given type with custom defined +// verbs. Custom verbs can have custom input and result types and also allow to +// use a sub-resource in a request instead of top-level resource type. +// +// Example: +// +// +genclient:method=UpdateScale,verb=update,subresource=scale,input=Scale,result=Scale +// +// type ReplicaSet struct { ... } +// +// The 'method=UpdateScale' is the name of the client function. +// The 'verb=update' here means the client function will use 'PUT' action. +// The 'subresource=scale' means we will use SubResource template to generate this client function. +// The 'input' is the input type used for creation (function argument). +// The 'result' (not needed in this case) is the result type returned from the +// client function. +// +type extension struct { + // VerbName is the name of the custom verb (Scale, Instantiate, etc..) + VerbName string + // VerbType is the type of the verb (only verbs from SupportedVerbs are + // supported) + VerbType string + // SubResourcePath defines a path to a sub-resource to use in the request. + // (optional) + SubResourcePath string + // InputTypeOverride overrides the input parameter type for the verb. By + // default the original type is used. Overriding the input type only works for + // "create" and "update" verb types. The given type must exists in the same + // package as the original type. + // (optional) + InputTypeOverride string + // ResultTypeOverride overrides the resulting object type for the verb. By + // default the original type is used. Overriding the result type works. + // (optional) + ResultTypeOverride string +} + +// IsSubresource indicates if this extension should generate the sub-resource. +func (e *extension) IsSubresource() bool { + return len(e.SubResourcePath) > 0 +} + +// HasVerb checks if the extension matches the given verb. +func (e *extension) HasVerb(verb string) bool { + return e.VerbType == verb +} + +// Input returns the input override package path and the type. +func (e *extension) Input() (string, string) { + parts := strings.Split(e.InputTypeOverride, ".") + return parts[len(parts)-1], strings.Join(parts[0:len(parts)-1], ".") +} + +// Result returns the result override package path and the type. +func (e *extension) Result() (string, string) { + parts := strings.Split(e.ResultTypeOverride, ".") + return parts[len(parts)-1], strings.Join(parts[0:len(parts)-1], ".") +} + +// Tags represents a genclient configuration for a single type. +type Tags struct { + // +genclient + GenerateClient bool + // +genclient:nonNamespaced + NonNamespaced bool + // +genclient:noStatus + NoStatus bool + // +genclient:noVerbs + NoVerbs bool + // +genclient:skipVerbs=get,update + // +genclient:onlyVerbs=create,delete + SkipVerbs []string + // +genclient:method=UpdateScale,verb=update,subresource=scale,input=Scale,result=Scale + Extensions []extension +} + +// HasVerb returns true if we should include the given verb in final client interface and +// generate the function for it. +func (t Tags) HasVerb(verb string) bool { + if len(t.SkipVerbs) == 0 { + return true + } + for _, s := range t.SkipVerbs { + if verb == s { + return false + } + } + return true +} + +// MustParseClientGenTags calls ParseClientGenTags but instead of returning error it panics. +func MustParseClientGenTags(lines []string) Tags { + tags, err := ParseClientGenTags(lines) + if err != nil { + panic(err.Error()) + } + return tags +} + +// ParseClientGenTags parse the provided genclient tags and validates that no unknown +// tags are provided. +func ParseClientGenTags(lines []string) (Tags, error) { + ret := Tags{} + values := types.ExtractCommentTags("+", lines) + value := []string{} + value, ret.GenerateClient = values["genclient"] + // Check the old format and error when used to avoid generating client when //+genclient=false + if len(value) > 0 && len(value[0]) > 0 { + return ret, fmt.Errorf("+genclient=%s is invalid, use //+genclient if you want to generate client or omit it when you want to disable generation", value) + } + _, ret.NonNamespaced = values[genClientPrefix+"nonNamespaced"] + // Check the old format and error when used + if value := values["nonNamespaced"]; len(value) > 0 && len(value[0]) > 0 { + return ret, fmt.Errorf("+nonNamespaced=%s is invalid, use //+genclient:nonNamespaced instead", value[0]) + } + _, ret.NoVerbs = values[genClientPrefix+"noVerbs"] + _, ret.NoStatus = values[genClientPrefix+"noStatus"] + onlyVerbs := []string{} + if _, isReadonly := values[genClientPrefix+"readonly"]; isReadonly { + onlyVerbs = ReadonlyVerbs + } + // Check the old format and error when used + if value := values["readonly"]; len(value) > 0 && len(value[0]) > 0 { + return ret, fmt.Errorf("+readonly=%s is invalid, use //+genclient:readonly instead", value[0]) + } + if v, exists := values[genClientPrefix+"skipVerbs"]; exists { + ret.SkipVerbs = strings.Split(v[0], ",") + } + if v, exists := values[genClientPrefix+"onlyVerbs"]; exists || len(onlyVerbs) > 0 { + if len(v) > 0 { + onlyVerbs = append(onlyVerbs, strings.Split(v[0], ",")...) + } + skipVerbs := []string{} + for _, m := range SupportedVerbs { + skip := true + for _, o := range onlyVerbs { + if o == m { + skip = false + break + } + } + // Check for conflicts + for _, v := range skipVerbs { + if v == m { + return ret, fmt.Errorf("verb %q used both in genclient:skipVerbs and genclient:onlyVerbs", v) + } + } + if skip { + skipVerbs = append(skipVerbs, m) + } + } + ret.SkipVerbs = skipVerbs + } + var err error + if ret.Extensions, err = parseClientExtensions(values); err != nil { + return ret, err + } + return ret, validateClientGenTags(values) +} + +func parseClientExtensions(tags map[string][]string) ([]extension, error) { + var ret []extension + for name, values := range tags { + if !strings.HasPrefix(name, genClientPrefix+"method") { + continue + } + for _, value := range values { + // the value comes in this form: "Foo,verb=create" + ext := extension{} + parts := strings.Split(value, ",") + if len(parts) == 0 { + return nil, fmt.Errorf("invalid of empty extension verb name: %q", value) + } + // The first part represents the name of the extension + ext.VerbName = parts[0] + if len(ext.VerbName) == 0 { + return nil, fmt.Errorf("must specify a verb name (// +genclient:method=Foo,verb=create)") + } + // Parse rest of the arguments + params := parts[1:] + for _, p := range params { + parts := strings.Split(p, "=") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid extension tag specification %q", p) + } + key, val := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) + if len(val) == 0 { + return nil, fmt.Errorf("empty value of %q for %q extension", key, ext.VerbName) + } + switch key { + case "verb": + ext.VerbType = val + case "subresource": + ext.SubResourcePath = val + case "input": + ext.InputTypeOverride = val + case "result": + ext.ResultTypeOverride = val + default: + return nil, fmt.Errorf("unknown extension configuration key %q", key) + } + } + // Validate resulting extension configuration + if len(ext.VerbType) == 0 { + return nil, fmt.Errorf("verb type must be specified (use '// +genclient:method=%s,verb=create')", ext.VerbName) + } + if len(ext.ResultTypeOverride) > 0 { + supported := false + for _, v := range resultTypeSupportedVerbs { + if ext.VerbType == v { + supported = true + break + } + } + if !supported { + return nil, fmt.Errorf("%s: result type is not supported for %q verbs (supported verbs: %#v)", ext.VerbName, ext.VerbType, resultTypeSupportedVerbs) + } + } + if len(ext.InputTypeOverride) > 0 { + supported := false + for _, v := range inputTypeSupportedVerbs { + if ext.VerbType == v { + supported = true + break + } + } + if !supported { + return nil, fmt.Errorf("%s: input type is not supported for %q verbs (supported verbs: %#v)", ext.VerbName, ext.VerbType, inputTypeSupportedVerbs) + } + } + for _, t := range unsupportedExtensionVerbs { + if ext.VerbType == t { + return nil, fmt.Errorf("verb %q is not supported by extension generator", ext.VerbType) + } + } + ret = append(ret, ext) + } + } + return ret, nil +} + +// validateTags validates that only supported genclient tags were provided. +func validateClientGenTags(values map[string][]string) error { + for _, k := range supportedTags { + delete(values, k) + } + for key := range values { + if strings.HasPrefix(key, strings.TrimSuffix(genClientPrefix, ":")) { + return errors.New("unknown tag detected: " + key) + } + } + return nil +} diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/main.go b/vendor/k8s.io/code-generator/cmd/client-gen/main.go new file mode 100644 index 0000000000..22c28e35f8 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/main.go @@ -0,0 +1,65 @@ +/* +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. +*/ + +// client-gen makes the individual typed clients using gengo. +package main + +import ( + "flag" + "path/filepath" + + "github.com/golang/glog" + "github.com/spf13/pflag" + "k8s.io/gengo/args" + + generatorargs "k8s.io/code-generator/cmd/client-gen/args" + "k8s.io/code-generator/cmd/client-gen/generators" + "k8s.io/code-generator/pkg/util" +) + +func main() { + genericArgs, customArgs := generatorargs.NewDefaults() + + // Override defaults. + // TODO: move this out of client-gen + genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath()) + genericArgs.OutputPackagePath = "k8s.io/kubernetes/pkg/client/clientset_generated/" + + genericArgs.AddFlags(pflag.CommandLine) + customArgs.AddFlags(pflag.CommandLine, "k8s.io/kubernetes/pkg/apis") // TODO: move this input path out of client-gen + flag.Set("logtostderr", "true") + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() + + // add group version package as input dirs for gengo + for _, pkg := range customArgs.Groups { + for _, v := range pkg.Versions { + genericArgs.InputDirs = append(genericArgs.InputDirs, v.Package) + } + } + + if err := generatorargs.Validate(genericArgs); err != nil { + glog.Fatalf("Error: %v", err) + } + + if err := genericArgs.Execute( + generators.NameSystems(), + generators.DefaultNameSystem(), + generators.Packages, + ); err != nil { + glog.Fatalf("Error: %v", err) + } +} diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/path/path.go b/vendor/k8s.io/code-generator/cmd/client-gen/path/path.go new file mode 100644 index 0000000000..19b269bdf2 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/path/path.go @@ -0,0 +1,31 @@ +/* +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 path + +import "strings" + +// Vendorless removes the longest match of "*/vendor/" from the front of p. +// It is useful if a package locates in vendor/, e.g., +// k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1, because gengo +// indexes the package with its import path, e.g., +// k8s.io/apimachinery/pkg/apis/meta/v1, +func Vendorless(p string) string { + if pos := strings.LastIndex(p, "/vendor/"); pos != -1 { + return p[pos+len("/vendor/"):] + } + return p +} diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/types/helpers.go b/vendor/k8s.io/code-generator/cmd/client-gen/types/helpers.go new file mode 100644 index 0000000000..33e6ac451b --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/types/helpers.go @@ -0,0 +1,123 @@ +/* +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 types + +import ( + "fmt" + "regexp" + "sort" + "strings" + + "k8s.io/gengo/namer" +) + +// ToGroupVersion turns "group/version" string into a GroupVersion struct. It reports error +// if it cannot parse the string. +func ToGroupVersion(gv string) (GroupVersion, error) { + // this can be the internal version for the legacy kube types + // TODO once we've cleared the last uses as strings, this special case should be removed. + if (len(gv) == 0) || (gv == "/") { + return GroupVersion{}, nil + } + + switch strings.Count(gv, "/") { + case 0: + return GroupVersion{Group(gv), ""}, nil + case 1: + i := strings.Index(gv, "/") + return GroupVersion{Group(gv[:i]), Version(gv[i+1:])}, nil + default: + return GroupVersion{}, fmt.Errorf("unexpected GroupVersion string: %v", gv) + } +} + +type sortableSliceOfVersions []string + +func (a sortableSliceOfVersions) Len() int { return len(a) } +func (a sortableSliceOfVersions) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a sortableSliceOfVersions) Less(i, j int) bool { + vi, vj := strings.TrimLeft(a[i], "v"), strings.TrimLeft(a[j], "v") + major := regexp.MustCompile("^[0-9]+") + viMajor, vjMajor := major.FindString(vi), major.FindString(vj) + viRemaining, vjRemaining := strings.TrimLeft(vi, viMajor), strings.TrimLeft(vj, vjMajor) + switch { + case len(viRemaining) == 0 && len(vjRemaining) == 0: + return viMajor < vjMajor + case len(viRemaining) == 0 && len(vjRemaining) != 0: + // stable version is greater than unstable version + return false + case len(viRemaining) != 0 && len(vjRemaining) == 0: + // stable version is greater than unstable version + return true + } + // neither are stable versions + if viMajor != vjMajor { + return viMajor < vjMajor + } + // assuming at most we have one alpha or one beta version, so if vi contains "alpha", it's the lesser one. + return strings.Contains(viRemaining, "alpha") +} + +// Determine the default version among versions. If a user calls a group client +// without specifying the version (e.g., c.Core(), instead of c.CoreV1()), the +// default version will be returned. +func defaultVersion(versions []PackageVersion) Version { + var versionStrings []string + for _, version := range versions { + versionStrings = append(versionStrings, version.Version.String()) + } + sort.Sort(sortableSliceOfVersions(versionStrings)) + return Version(versionStrings[len(versionStrings)-1]) +} + +// ToGroupVersionInfo is a helper function used by generators for groups. +func ToGroupVersionInfo(groups []GroupVersions, groupGoNames map[GroupVersion]string) []GroupVersionInfo { + var groupVersionPackages []GroupVersionInfo + for _, group := range groups { + defaultVersion := defaultVersion(group.Versions) + for _, version := range group.Versions { + groupGoName := groupGoNames[GroupVersion{Group: group.Group, Version: version.Version}] + groupVersionPackages = append(groupVersionPackages, GroupVersionInfo{ + Group: Group(namer.IC(group.Group.NonEmpty())), + Version: Version(namer.IC(version.Version.String())), + PackageAlias: strings.ToLower(groupGoName + version.Version.NonEmpty()), + IsDefaultVersion: version.Version == defaultVersion && version.Version != "", + GroupGoName: groupGoName, + LowerCaseGroupGoName: namer.IL(groupGoName), + }) + } + } + return groupVersionPackages +} + +func ToGroupInstallPackages(groups []GroupVersions, groupGoNames map[GroupVersion]string) []GroupInstallPackage { + var groupInstallPackages []GroupInstallPackage + for _, group := range groups { + defaultVersion := defaultVersion(group.Versions) + groupGoName := groupGoNames[GroupVersion{Group: group.Group, Version: defaultVersion}] + groupInstallPackages = append(groupInstallPackages, GroupInstallPackage{ + Group: Group(namer.IC(group.Group.NonEmpty())), + InstallPackageAlias: strings.ToLower(groupGoName), + }) + } + return groupInstallPackages +} + +// NormalizeGroupVersion calls normalizes the GroupVersion. +//func NormalizeGroupVersion(gv GroupVersion) GroupVersion { +// return GroupVersion{Group: gv.Group.NonEmpty(), Version: gv.Version, NonEmptyVersion: normalization.Version(gv.Version)} +//} diff --git a/vendor/k8s.io/code-generator/cmd/client-gen/types/types.go b/vendor/k8s.io/code-generator/cmd/client-gen/types/types.go new file mode 100644 index 0000000000..17fd6e92a7 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/client-gen/types/types.go @@ -0,0 +1,78 @@ +/* +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 types + +type Version string + +func (v Version) String() string { + return string(v) +} + +func (v Version) NonEmpty() string { + if v == "" { + return "internalVersion" + } + return v.String() +} + +type Group string + +func (g Group) String() string { + return string(g) +} + +func (g Group) NonEmpty() string { + if g == "api" { + return "core" + } + return string(g) +} + +type PackageVersion struct { + Version + // The fully qualified package, e.g. k8s.io/kubernetes/pkg/apis/apps, where the types.go is found. + Package string +} + +type GroupVersion struct { + Group Group + Version Version +} + +type GroupVersions struct { + // The name of the package for this group, e.g. apps. + PackageName string + Group Group + Versions []PackageVersion +} + +// GroupVersionInfo contains all the info around a group version. +type GroupVersionInfo struct { + Group Group + Version Version + // If a user calls a group client without specifying the version (e.g., + // c.Core(), instead of c.CoreV1()), the default version will be returned. + IsDefaultVersion bool + PackageAlias string + GroupGoName string + LowerCaseGroupGoName string +} + +type GroupInstallPackage struct { + Group Group + InstallPackageAlias string +} diff --git a/vendor/k8s.io/code-generator/cmd/deepcopy-gen/args/args.go b/vendor/k8s.io/code-generator/cmd/deepcopy-gen/args/args.go new file mode 100644 index 0000000000..789713012a --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/deepcopy-gen/args/args.go @@ -0,0 +1,54 @@ +/* +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 args + +import ( + "fmt" + + "github.com/spf13/pflag" + "k8s.io/gengo/args" + "k8s.io/gengo/examples/deepcopy-gen/generators" +) + +// CustomArgs is used by the gengo framework to pass args specific to this generator. +type CustomArgs generators.CustomArgs + +// NewDefaults returns default arguments for the generator. +func NewDefaults() (*args.GeneratorArgs, *CustomArgs) { + genericArgs := args.Default().WithoutDefaultFlagParsing() + customArgs := &CustomArgs{} + genericArgs.CustomArgs = (*generators.CustomArgs)(customArgs) // convert to upstream type to make type-casts work there + genericArgs.OutputFileBaseName = "deepcopy_generated" + return genericArgs, customArgs +} + +// AddFlags add the generator flags to the flag set. +func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) { + pflag.CommandLine.StringSliceVar(&ca.BoundingDirs, "bounding-dirs", ca.BoundingDirs, + "Comma-separated list of import paths which bound the types for which deep-copies will be generated.") +} + +// Validate checks the given arguments. +func Validate(genericArgs *args.GeneratorArgs) error { + _ = genericArgs.CustomArgs.(*generators.CustomArgs) + + if len(genericArgs.OutputFileBaseName) == 0 { + return fmt.Errorf("output file base name cannot be empty") + } + + return nil +} diff --git a/vendor/k8s.io/code-generator/cmd/deepcopy-gen/main.go b/vendor/k8s.io/code-generator/cmd/deepcopy-gen/main.go new file mode 100644 index 0000000000..cce65b772f --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/deepcopy-gen/main.go @@ -0,0 +1,84 @@ +/* +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. +*/ + +// deepcopy-gen is a tool for auto-generating DeepCopy functions. +// +// Given a list of input directories, it will generate functions that +// efficiently perform a full deep-copy of each type. For any type that +// offers a `.DeepCopy()` method, it will simply call that. Otherwise it will +// use standard value assignment whenever possible. If that is not possible it +// will try to call its own generated copy function for the type, if the type is +// within the allowed root packages. Failing that, it will fall back on +// `conversion.Cloner.DeepCopy(val)` to make the copy. The resulting file will +// be stored in the same directory as the processed source package. +// +// Generation is governed by comment tags in the source. Any package may +// request DeepCopy generation by including a comment in the file-comments of +// one file, of the form: +// // +k8s:deepcopy-gen=package +// +// DeepCopy functions can be generated for individual types, rather than the +// entire package by specifying a comment on the type definion of the form: +// // +k8s:deepcopy-gen=true +// +// When generating for a whole package, individual types may opt out of +// DeepCopy generation by specifying a comment on the of the form: +// // +k8s:deepcopy-gen=false +// +// Note that registration is a whole-package option, and is not available for +// individual types. +package main + +import ( + "flag" + "path/filepath" + + "github.com/golang/glog" + "github.com/spf13/pflag" + "k8s.io/gengo/args" + "k8s.io/gengo/examples/deepcopy-gen/generators" + + generatorargs "k8s.io/code-generator/cmd/deepcopy-gen/args" + "k8s.io/code-generator/pkg/util" +) + +func main() { + genericArgs, customArgs := generatorargs.NewDefaults() + + // Override defaults. + // TODO: move this out of deepcopy-gen + genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath()) + + genericArgs.AddFlags(pflag.CommandLine) + customArgs.AddFlags(pflag.CommandLine) + flag.Set("logtostderr", "true") + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() + + if err := generatorargs.Validate(genericArgs); err != nil { + glog.Fatalf("Error: %v", err) + } + + // Run it. + if err := genericArgs.Execute( + generators.NameSystems(), + generators.DefaultNameSystem(), + generators.Packages, + ); err != nil { + glog.Fatalf("Error: %v", err) + } + glog.V(2).Info("Completed successfully.") +} diff --git a/vendor/k8s.io/code-generator/cmd/defaulter-gen/args/args.go b/vendor/k8s.io/code-generator/cmd/defaulter-gen/args/args.go new file mode 100644 index 0000000000..3c5a042c7c --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/defaulter-gen/args/args.go @@ -0,0 +1,54 @@ +/* +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 args + +import ( + "fmt" + + "github.com/spf13/pflag" + "k8s.io/gengo/args" + "k8s.io/gengo/examples/defaulter-gen/generators" +) + +// CustomArgs is used by the gengo framework to pass args specific to this generator. +type CustomArgs generators.CustomArgs + +// NewDefaults returns default arguments for the generator. +func NewDefaults() (*args.GeneratorArgs, *CustomArgs) { + genericArgs := args.Default().WithoutDefaultFlagParsing() + customArgs := &CustomArgs{} + genericArgs.CustomArgs = (*generators.CustomArgs)(customArgs) // convert to upstream type to make type-casts work there + genericArgs.OutputFileBaseName = "zz_generated.defaults" + return genericArgs, customArgs +} + +// AddFlags add the generator flags to the flag set. +func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) { + pflag.CommandLine.StringSliceVar(&ca.ExtraPeerDirs, "extra-peer-dirs", ca.ExtraPeerDirs, + "Comma-separated list of import paths which are considered, after tag-specified peers, for conversions.") +} + +// Validate checks the given arguments. +func Validate(genericArgs *args.GeneratorArgs) error { + _ = genericArgs.CustomArgs.(*generators.CustomArgs) + + if len(genericArgs.OutputFileBaseName) == 0 { + return fmt.Errorf("output file base name cannot be empty") + } + + return nil +} diff --git a/vendor/k8s.io/code-generator/cmd/defaulter-gen/main.go b/vendor/k8s.io/code-generator/cmd/defaulter-gen/main.go new file mode 100644 index 0000000000..9d33f700b3 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/defaulter-gen/main.go @@ -0,0 +1,83 @@ +/* +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. +*/ + +// defaulter-gen is a tool for auto-generating Defaulter functions. +// +// Given a list of input directories, it will scan for top level types +// and generate efficient defaulters for an entire object from the sum +// of the SetDefault_* methods contained in the object tree. +// +// Generation is governed by comment tags in the source. Any package may +// request defaulter generation by including one or more comment tags at +// the package comment level: +// +// // +k8s:defaulter-gen= +// +// which will create defaulters for any type that contains the provided +// field name (if the type has defaulters). Any type may request explicit +// defaulting by providing the comment tag: +// +// // +k8s:defaulter-gen=true|false +// +// An existing defaulter method (`SetDefaults_TYPE`) can provide the +// comment tag: +// +// // +k8s:defaulter-gen=covers +// +// to indicate that the defaulter does not or should not call any nested +// defaulters. +package main + +import ( + "flag" + "path/filepath" + + "github.com/golang/glog" + "github.com/spf13/pflag" + "k8s.io/gengo/args" + "k8s.io/gengo/examples/defaulter-gen/generators" + + generatorargs "k8s.io/code-generator/cmd/defaulter-gen/args" + "k8s.io/code-generator/pkg/util" +) + +func main() { + genericArgs, customArgs := generatorargs.NewDefaults() + + // Override defaults. + // TODO: move this out of defaulter-gen + genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath()) + + genericArgs.AddFlags(pflag.CommandLine) + customArgs.AddFlags(pflag.CommandLine) + flag.Set("logtostderr", "true") + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() + + if err := generatorargs.Validate(genericArgs); err != nil { + glog.Fatalf("Error: %v", err) + } + + // Run it. + if err := genericArgs.Execute( + generators.NameSystems(), + generators.DefaultNameSystem(), + generators.Packages, + ); err != nil { + glog.Fatalf("Error: %v", err) + } + glog.V(2).Info("Completed successfully.") +} diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/args/args.go b/vendor/k8s.io/code-generator/cmd/informer-gen/args/args.go new file mode 100644 index 0000000000..ba7f720917 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/args/args.go @@ -0,0 +1,77 @@ +/* +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 args + +import ( + "fmt" + "path" + + "github.com/spf13/pflag" + codegenutil "k8s.io/code-generator/pkg/util" + "k8s.io/gengo/args" +) + +// CustomArgs is used by the gengo framework to pass args specific to this generator. +type CustomArgs struct { + VersionedClientSetPackage string + InternalClientSetPackage string + ListersPackage string + SingleDirectory bool +} + +// NewDefaults returns default arguments for the generator. +func NewDefaults() (*args.GeneratorArgs, *CustomArgs) { + genericArgs := args.Default().WithoutDefaultFlagParsing() + customArgs := &CustomArgs{ + SingleDirectory: false, + } + genericArgs.CustomArgs = customArgs + + if pkg := codegenutil.CurrentPackage(); len(pkg) != 0 { + genericArgs.OutputPackagePath = path.Join(pkg, "pkg/client/informers") + customArgs.VersionedClientSetPackage = path.Join(pkg, "pkg/client/clientset/versioned") + customArgs.InternalClientSetPackage = path.Join(pkg, "pkg/client/clientset/internalversion") + customArgs.ListersPackage = path.Join(pkg, "pkg/client/listers") + } + + return genericArgs, customArgs +} + +// AddFlags add the generator flags to the flag set. +func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&ca.InternalClientSetPackage, "internal-clientset-package", ca.InternalClientSetPackage, "the full package name for the internal clientset to use") + fs.StringVar(&ca.VersionedClientSetPackage, "versioned-clientset-package", ca.VersionedClientSetPackage, "the full package name for the versioned clientset to use") + fs.StringVar(&ca.ListersPackage, "listers-package", ca.ListersPackage, "the full package name for the listers to use") + fs.BoolVar(&ca.SingleDirectory, "single-directory", ca.SingleDirectory, "if true, omit the intermediate \"internalversion\" and \"externalversions\" subdirectories") +} + +// Validate checks the given arguments. +func Validate(genericArgs *args.GeneratorArgs) error { + customArgs := genericArgs.CustomArgs.(*CustomArgs) + + if len(genericArgs.OutputPackagePath) == 0 { + return fmt.Errorf("output package cannot be empty") + } + if len(customArgs.VersionedClientSetPackage) == 0 { + return fmt.Errorf("versioned clientset package cannot be empty") + } + if len(customArgs.ListersPackage) == 0 { + return fmt.Errorf("listers package cannot be empty") + } + + return nil +} diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/generators/factory.go b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/factory.go new file mode 100644 index 0000000000..5c557db739 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/factory.go @@ -0,0 +1,258 @@ +/* +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 generators + +import ( + "io" + "path" + + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "github.com/golang/glog" +) + +// factoryGenerator produces a file of listers for a given GroupVersion and +// type. +type factoryGenerator struct { + generator.DefaultGen + outputPackage string + imports namer.ImportTracker + groupVersions map[string]clientgentypes.GroupVersions + gvGoNames map[string]string + clientSetPackage string + internalInterfacesPackage string + filtered bool +} + +var _ generator.Generator = &factoryGenerator{} + +func (g *factoryGenerator) Filter(c *generator.Context, t *types.Type) bool { + if !g.filtered { + g.filtered = true + return true + } + return false +} + +func (g *factoryGenerator) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *factoryGenerator) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.imports.ImportLines()...) + return +} + +func (g *factoryGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "{{", "}}") + + glog.V(5).Infof("processing type %v", t) + + gvInterfaces := make(map[string]*types.Type) + gvNewFuncs := make(map[string]*types.Type) + for groupPkgName := range g.groupVersions { + gvInterfaces[groupPkgName] = c.Universe.Type(types.Name{Package: path.Join(g.outputPackage, groupPkgName), Name: "Interface"}) + gvNewFuncs[groupPkgName] = c.Universe.Function(types.Name{Package: path.Join(g.outputPackage, groupPkgName), Name: "New"}) + } + m := map[string]interface{}{ + "cacheSharedIndexInformer": c.Universe.Type(cacheSharedIndexInformer), + "groupVersions": g.groupVersions, + "gvInterfaces": gvInterfaces, + "gvNewFuncs": gvNewFuncs, + "gvGoNames": g.gvGoNames, + "interfacesNewInformerFunc": c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "NewInformerFunc"}), + "interfacesTweakListOptionsFunc": c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "TweakListOptionsFunc"}), + "informerFactoryInterface": c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "SharedInformerFactory"}), + "clientSetInterface": c.Universe.Type(types.Name{Package: g.clientSetPackage, Name: "Interface"}), + "reflectType": c.Universe.Type(reflectType), + "runtimeObject": c.Universe.Type(runtimeObject), + "schemaGroupVersionResource": c.Universe.Type(schemaGroupVersionResource), + "syncMutex": c.Universe.Type(syncMutex), + "timeDuration": c.Universe.Type(timeDuration), + "namespaceAll": c.Universe.Type(metav1NamespaceAll), + "object": c.Universe.Type(metav1Object), + } + + sw.Do(sharedInformerFactoryStruct, m) + sw.Do(sharedInformerFactoryInterface, m) + + return sw.Error() +} + +var sharedInformerFactoryStruct = ` +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client {{.clientSetInterface|raw}} + namespace string + tweakListOptions {{.interfacesTweakListOptionsFunc|raw}} + lock {{.syncMutex|raw}} + defaultResync {{.timeDuration|raw}} + customResync map[{{.reflectType|raw}}]{{.timeDuration|raw}} + + informers map[{{.reflectType|raw}}]{{.cacheSharedIndexInformer|raw}} + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[{{.reflectType|raw}}]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[{{.object|raw}}]{{.timeDuration|raw}}) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client {{.clientSetInterface|raw}}, defaultResync {{.timeDuration|raw}}) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client {{.clientSetInterface|raw}}, defaultResync {{.timeDuration|raw}}, namespace string, tweakListOptions {{.interfacesTweakListOptionsFunc|raw}}) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client {{.clientSetInterface|raw}}, defaultResync {{.timeDuration|raw}}, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[{{.reflectType|raw}}]{{.cacheSharedIndexInformer|raw}}), + startedInformers: make(map[{{.reflectType|raw}}]bool), + customResync: make(map[{{.reflectType|raw}}]{{.timeDuration|raw}}), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func()map[reflect.Type]cache.SharedIndexInformer{ + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj {{.runtimeObject|raw}}, newFunc {{.interfacesNewInformerFunc|raw}}) {{.cacheSharedIndexInformer|raw}} { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +` + +var sharedInformerFactoryInterface = ` +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + {{.informerFactoryInterface|raw}} + ForResource(resource {{.schemaGroupVersionResource|raw}}) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + {{$gvInterfaces := .gvInterfaces}} + {{$gvGoNames := .gvGoNames}} + {{range $groupName, $group := .groupVersions}}{{index $gvGoNames $groupName}}() {{index $gvInterfaces $groupName|raw}} + {{end}} +} + +{{$gvNewFuncs := .gvNewFuncs}} +{{$gvGoNames := .gvGoNames}} +{{range $groupPkgName, $group := .groupVersions}} +func (f *sharedInformerFactory) {{index $gvGoNames $groupPkgName}}() {{index $gvInterfaces $groupPkgName|raw}} { + return {{index $gvNewFuncs $groupPkgName|raw}}(f, f.namespace, f.tweakListOptions) +} +{{end}} +` diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/generators/factoryinterface.go b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/factoryinterface.go new file mode 100644 index 0000000000..c781804387 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/factoryinterface.go @@ -0,0 +1,88 @@ +/* +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 generators + +import ( + "io" + + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "github.com/golang/glog" +) + +// factoryInterfaceGenerator produces a file of interfaces used to break a dependency cycle for +// informer registration +type factoryInterfaceGenerator struct { + generator.DefaultGen + outputPackage string + imports namer.ImportTracker + clientSetPackage string + filtered bool +} + +var _ generator.Generator = &factoryInterfaceGenerator{} + +func (g *factoryInterfaceGenerator) Filter(c *generator.Context, t *types.Type) bool { + if !g.filtered { + g.filtered = true + return true + } + return false +} + +func (g *factoryInterfaceGenerator) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *factoryInterfaceGenerator) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.imports.ImportLines()...) + return +} + +func (g *factoryInterfaceGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "{{", "}}") + + glog.V(5).Infof("processing type %v", t) + + m := map[string]interface{}{ + "cacheSharedIndexInformer": c.Universe.Type(cacheSharedIndexInformer), + "clientSetPackage": c.Universe.Type(types.Name{Package: g.clientSetPackage, Name: "Interface"}), + "runtimeObject": c.Universe.Type(runtimeObject), + "timeDuration": c.Universe.Type(timeDuration), + "v1ListOptions": c.Universe.Type(v1ListOptions), + } + + sw.Do(externalSharedInformerFactoryInterface, m) + + return sw.Error() +} + +var externalSharedInformerFactoryInterface = ` +type NewInformerFunc func({{.clientSetPackage|raw}}, {{.timeDuration|raw}}) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj {{.runtimeObject|raw}}, newFunc NewInformerFunc) {{.cacheSharedIndexInformer|raw}} +} + +type TweakListOptionsFunc func(*{{.v1ListOptions|raw}}) +` diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/generators/generic.go b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/generic.go new file mode 100644 index 0000000000..54632de053 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/generic.go @@ -0,0 +1,180 @@ +/* +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 generators + +import ( + "io" + "sort" + "strings" + + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" +) + +// genericGenerator generates the generic informer. +type genericGenerator struct { + generator.DefaultGen + outputPackage string + imports namer.ImportTracker + groupVersions map[string]clientgentypes.GroupVersions + groupGoNames map[string]string + typesForGroupVersion map[clientgentypes.GroupVersion][]*types.Type + filtered bool +} + +var _ generator.Generator = &genericGenerator{} + +func (g *genericGenerator) Filter(c *generator.Context, t *types.Type) bool { + if !g.filtered { + g.filtered = true + return true + } + return false +} + +func (g *genericGenerator) Namers(c *generator.Context) namer.NameSystems { + pluralExceptions := map[string]string{ + "Endpoints": "Endpoints", + } + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + "allLowercasePlural": namer.NewAllLowercasePluralNamer(pluralExceptions), + "publicPlural": namer.NewPublicPluralNamer(pluralExceptions), + } +} + +func (g *genericGenerator) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.imports.ImportLines()...) + imports = append(imports, "fmt") + return +} + +type group struct { + GroupGoName string + Name string + Versions []*version +} + +type groupSort []group + +func (g groupSort) Len() int { return len(g) } +func (g groupSort) Less(i, j int) bool { return strings.ToLower(g[i].Name) < strings.ToLower(g[j].Name) } +func (g groupSort) Swap(i, j int) { g[i], g[j] = g[j], g[i] } + +type version struct { + Name string + GoName string + Resources []*types.Type +} + +type versionSort []*version + +func (v versionSort) Len() int { return len(v) } +func (v versionSort) Less(i, j int) bool { + return strings.ToLower(v[i].Name) < strings.ToLower(v[j].Name) +} +func (v versionSort) Swap(i, j int) { v[i], v[j] = v[j], v[i] } + +func (g *genericGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "{{", "}}") + + groups := []group{} + schemeGVs := make(map[*version]*types.Type) + + orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)} + for groupPackageName, groupVersions := range g.groupVersions { + group := group{ + GroupGoName: g.groupGoNames[groupPackageName], + Name: groupVersions.Group.NonEmpty(), + Versions: []*version{}, + } + for _, v := range groupVersions.Versions { + gv := clientgentypes.GroupVersion{Group: groupVersions.Group, Version: v.Version} + version := &version{ + Name: v.Version.NonEmpty(), + GoName: namer.IC(v.Version.NonEmpty()), + Resources: orderer.OrderTypes(g.typesForGroupVersion[gv]), + } + schemeGVs[version] = c.Universe.Variable(types.Name{Package: g.typesForGroupVersion[gv][0].Name.Package, Name: "SchemeGroupVersion"}) + group.Versions = append(group.Versions, version) + } + sort.Sort(versionSort(group.Versions)) + groups = append(groups, group) + } + sort.Sort(groupSort(groups)) + + m := map[string]interface{}{ + "cacheGenericLister": c.Universe.Type(cacheGenericLister), + "cacheNewGenericLister": c.Universe.Function(cacheNewGenericLister), + "cacheSharedIndexInformer": c.Universe.Type(cacheSharedIndexInformer), + "groups": groups, + "schemeGVs": schemeGVs, + "schemaGroupResource": c.Universe.Type(schemaGroupResource), + "schemaGroupVersionResource": c.Universe.Type(schemaGroupVersionResource), + } + + sw.Do(genericInformer, m) + sw.Do(forResource, m) + + return sw.Error() +} + +var genericInformer = ` +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() {{.cacheSharedIndexInformer|raw}} + Lister() {{.cacheGenericLister|raw}} +} + +type genericInformer struct { + informer {{.cacheSharedIndexInformer|raw}} + resource {{.schemaGroupResource|raw}} +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() {{.cacheSharedIndexInformer|raw}} { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() {{.cacheGenericLister|raw}} { + return {{.cacheNewGenericLister|raw}}(f.Informer().GetIndexer(), f.resource) +} +` + +var forResource = ` +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource {{.schemaGroupVersionResource|raw}}) (GenericInformer, error) { + switch resource { + {{range $group := .groups -}}{{$GroupGoName := .GroupGoName -}} + {{range $version := .Versions -}} + // Group={{$group.Name}}, Version={{.Name}} + {{range .Resources -}} + case {{index $.schemeGVs $version|raw}}.WithResource("{{.|allLowercasePlural}}"): + return &genericInformer{resource: resource.GroupResource(), informer: f.{{$GroupGoName}}().{{$version.GoName}}().{{.|publicPlural}}().Informer()}, nil + {{end}} + {{end}} + {{end -}} + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} +` diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/generators/groupinterface.go b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/groupinterface.go new file mode 100644 index 0000000000..0bba93c4b2 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/groupinterface.go @@ -0,0 +1,118 @@ +/* +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 generators + +import ( + "io" + "path/filepath" + "strings" + + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" +) + +// groupInterfaceGenerator generates the per-group interface file. +type groupInterfaceGenerator struct { + generator.DefaultGen + outputPackage string + imports namer.ImportTracker + groupVersions clientgentypes.GroupVersions + filtered bool + internalInterfacesPackage string +} + +var _ generator.Generator = &groupInterfaceGenerator{} + +func (g *groupInterfaceGenerator) Filter(c *generator.Context, t *types.Type) bool { + if !g.filtered { + g.filtered = true + return true + } + return false +} + +func (g *groupInterfaceGenerator) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *groupInterfaceGenerator) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.imports.ImportLines()...) + return +} + +type versionData struct { + Name string + Interface *types.Type + New *types.Type +} + +func (g *groupInterfaceGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + + versions := make([]versionData, 0, len(g.groupVersions.Versions)) + for _, version := range g.groupVersions.Versions { + gv := clientgentypes.GroupVersion{Group: g.groupVersions.Group, Version: version.Version} + versionPackage := filepath.Join(g.outputPackage, strings.ToLower(gv.Version.NonEmpty())) + iface := c.Universe.Type(types.Name{Package: versionPackage, Name: "Interface"}) + versions = append(versions, versionData{ + Name: namer.IC(version.Version.NonEmpty()), + Interface: iface, + New: c.Universe.Function(types.Name{Package: versionPackage, Name: "New"}), + }) + } + m := map[string]interface{}{ + "interfacesTweakListOptionsFunc": c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "TweakListOptionsFunc"}), + "interfacesSharedInformerFactory": c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "SharedInformerFactory"}), + "versions": versions, + } + + sw.Do(groupTemplate, m) + + return sw.Error() +} + +var groupTemplate = ` +// Interface provides access to each of this group's versions. +type Interface interface { + $range .versions -$ + // $.Name$ provides access to shared informers for resources in $.Name$. + $.Name$() $.Interface|raw$ + $end$ +} + +type group struct { + factory $.interfacesSharedInformerFactory|raw$ + namespace string + tweakListOptions $.interfacesTweakListOptionsFunc|raw$ +} + +// New returns a new Interface. +func New(f $.interfacesSharedInformerFactory|raw$, namespace string, tweakListOptions $.interfacesTweakListOptionsFunc|raw$) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +$range .versions$ +// $.Name$ returns a new $.Interface|raw$. +func (g *group) $.Name$() $.Interface|raw$ { + return $.New|raw$(g.factory, g.namespace, g.tweakListOptions) +} +$end$ +` diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/generators/informer.go b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/informer.go new file mode 100644 index 0000000000..88cc08df52 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/informer.go @@ -0,0 +1,186 @@ +/* +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 generators + +import ( + "fmt" + "io" + "strings" + + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/code-generator/cmd/client-gen/generators/util" + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + + "github.com/golang/glog" +) + +// informerGenerator produces a file of listers for a given GroupVersion and +// type. +type informerGenerator struct { + generator.DefaultGen + outputPackage string + groupPkgName string + groupVersion clientgentypes.GroupVersion + groupGoName string + typeToGenerate *types.Type + imports namer.ImportTracker + clientSetPackage string + listersPackage string + internalInterfacesPackage string +} + +var _ generator.Generator = &informerGenerator{} + +func (g *informerGenerator) Filter(c *generator.Context, t *types.Type) bool { + return t == g.typeToGenerate +} + +func (g *informerGenerator) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *informerGenerator) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.imports.ImportLines()...) + return +} + +func (g *informerGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + + glog.V(5).Infof("processing type %v", t) + + listerPackage := fmt.Sprintf("%s/%s/%s", g.listersPackage, g.groupPkgName, strings.ToLower(g.groupVersion.Version.NonEmpty())) + clientSetInterface := c.Universe.Type(types.Name{Package: g.clientSetPackage, Name: "Interface"}) + informerFor := "InformerFor" + + tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + if err != nil { + return err + } + + m := map[string]interface{}{ + "apiScheme": c.Universe.Type(apiScheme), + "cacheIndexers": c.Universe.Type(cacheIndexers), + "cacheListWatch": c.Universe.Type(cacheListWatch), + "cacheMetaNamespaceIndexFunc": c.Universe.Function(cacheMetaNamespaceIndexFunc), + "cacheNamespaceIndex": c.Universe.Variable(cacheNamespaceIndex), + "cacheNewSharedIndexInformer": c.Universe.Function(cacheNewSharedIndexInformer), + "cacheSharedIndexInformer": c.Universe.Type(cacheSharedIndexInformer), + "clientSetInterface": clientSetInterface, + "group": namer.IC(g.groupGoName), + "informerFor": informerFor, + "interfacesTweakListOptionsFunc": c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "TweakListOptionsFunc"}), + "interfacesSharedInformerFactory": c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "SharedInformerFactory"}), + "listOptions": c.Universe.Type(listOptions), + "lister": c.Universe.Type(types.Name{Package: listerPackage, Name: t.Name.Name + "Lister"}), + "namespaceAll": c.Universe.Type(metav1NamespaceAll), + "namespaced": !tags.NonNamespaced, + "newLister": c.Universe.Function(types.Name{Package: listerPackage, Name: "New" + t.Name.Name + "Lister"}), + "runtimeObject": c.Universe.Type(runtimeObject), + "timeDuration": c.Universe.Type(timeDuration), + "type": t, + "v1ListOptions": c.Universe.Type(v1ListOptions), + "version": namer.IC(g.groupVersion.Version.String()), + "watchInterface": c.Universe.Type(watchInterface), + } + + sw.Do(typeInformerInterface, m) + sw.Do(typeInformerStruct, m) + sw.Do(typeInformerPublicConstructor, m) + sw.Do(typeFilteredInformerPublicConstructor, m) + sw.Do(typeInformerConstructor, m) + sw.Do(typeInformerInformer, m) + sw.Do(typeInformerLister, m) + + return sw.Error() +} + +var typeInformerInterface = ` +// $.type|public$Informer provides access to a shared informer and lister for +// $.type|publicPlural$. +type $.type|public$Informer interface { + Informer() $.cacheSharedIndexInformer|raw$ + Lister() $.lister|raw$ +} +` + +var typeInformerStruct = ` +type $.type|private$Informer struct { + factory $.interfacesSharedInformerFactory|raw$ + tweakListOptions $.interfacesTweakListOptionsFunc|raw$ + $if .namespaced$namespace string$end$ +} +` + +var typeInformerPublicConstructor = ` +// New$.type|public$Informer constructs a new informer for $.type|public$ type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func New$.type|public$Informer(client $.clientSetInterface|raw$$if .namespaced$, namespace string$end$, resyncPeriod $.timeDuration|raw$, indexers $.cacheIndexers|raw$) $.cacheSharedIndexInformer|raw$ { + return NewFiltered$.type|public$Informer(client$if .namespaced$, namespace$end$, resyncPeriod, indexers, nil) +} +` + +var typeFilteredInformerPublicConstructor = ` +// NewFiltered$.type|public$Informer constructs a new informer for $.type|public$ type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFiltered$.type|public$Informer(client $.clientSetInterface|raw$$if .namespaced$, namespace string$end$, resyncPeriod $.timeDuration|raw$, indexers $.cacheIndexers|raw$, tweakListOptions $.interfacesTweakListOptionsFunc|raw$) $.cacheSharedIndexInformer|raw$ { + return $.cacheNewSharedIndexInformer|raw$( + &$.cacheListWatch|raw${ + ListFunc: func(options $.v1ListOptions|raw$) ($.runtimeObject|raw$, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.$.group$$.version$().$.type|publicPlural$($if .namespaced$namespace$end$).List(options) + }, + WatchFunc: func(options $.v1ListOptions|raw$) ($.watchInterface|raw$, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.$.group$$.version$().$.type|publicPlural$($if .namespaced$namespace$end$).Watch(options) + }, + }, + &$.type|raw${}, + resyncPeriod, + indexers, + ) +} +` + +var typeInformerConstructor = ` +func (f *$.type|private$Informer) defaultInformer(client $.clientSetInterface|raw$, resyncPeriod $.timeDuration|raw$) $.cacheSharedIndexInformer|raw$ { + return NewFiltered$.type|public$Informer(client$if .namespaced$, f.namespace$end$, resyncPeriod, $.cacheIndexers|raw${$.cacheNamespaceIndex|raw$: $.cacheMetaNamespaceIndexFunc|raw$}, f.tweakListOptions) +} +` + +var typeInformerInformer = ` +func (f *$.type|private$Informer) Informer() $.cacheSharedIndexInformer|raw$ { + return f.factory.$.informerFor$(&$.type|raw${}, f.defaultInformer) +} +` + +var typeInformerLister = ` +func (f *$.type|private$Informer) Lister() $.lister|raw$ { + return $.newLister|raw$(f.Informer().GetIndexer()) +} +` diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/generators/packages.go b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/packages.go new file mode 100644 index 0000000000..2cc0372f89 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/packages.go @@ -0,0 +1,352 @@ +/* +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 generators + +import ( + "fmt" + "path" + "path/filepath" + "strings" + + "github.com/golang/glog" + "k8s.io/gengo/args" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/code-generator/cmd/client-gen/generators/util" + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + informergenargs "k8s.io/code-generator/cmd/informer-gen/args" +) + +// NameSystems returns the name system used by the generators in this package. +func NameSystems() namer.NameSystems { + pluralExceptions := map[string]string{ + "Endpoints": "Endpoints", + } + return namer.NameSystems{ + "public": namer.NewPublicNamer(0), + "private": namer.NewPrivateNamer(0), + "raw": namer.NewRawNamer("", nil), + "publicPlural": namer.NewPublicPluralNamer(pluralExceptions), + "allLowercasePlural": namer.NewAllLowercasePluralNamer(pluralExceptions), + "lowercaseSingular": &lowercaseSingularNamer{}, + } +} + +// lowercaseSingularNamer implements Namer +type lowercaseSingularNamer struct{} + +// Name returns t's name in all lowercase. +func (n *lowercaseSingularNamer) Name(t *types.Type) string { + return strings.ToLower(t.Name.Name) +} + +// DefaultNameSystem returns the default name system for ordering the types to be +// processed by the generators in this package. +func DefaultNameSystem() string { + return "public" +} + +// objectMetaForPackage returns the type of ObjectMeta used by package p. +func objectMetaForPackage(p *types.Package) (*types.Type, bool, error) { + generatingForPackage := false + for _, t := range p.Types { + if !util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)).GenerateClient { + continue + } + generatingForPackage = true + for _, member := range t.Members { + if member.Name == "ObjectMeta" { + return member.Type, isInternal(member), nil + } + } + } + if generatingForPackage { + return nil, false, fmt.Errorf("unable to find ObjectMeta for any types in package %s", p.Path) + } + return nil, false, nil +} + +// isInternal returns true if the tags for a member do not contain a json tag +func isInternal(m types.Member) bool { + return !strings.Contains(m.Tags, "json") +} + +func packageForInternalInterfaces(base string) string { + return filepath.Join(base, "internalinterfaces") +} + +func vendorless(p string) string { + if pos := strings.LastIndex(p, "/vendor/"); pos != -1 { + return p[pos+len("/vendor/"):] + } + return p +} + +// Packages makes the client package definition. +func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { + boilerplate, err := arguments.LoadGoBoilerplate() + if err != nil { + glog.Fatalf("Failed loading boilerplate: %v", err) + } + + customArgs, ok := arguments.CustomArgs.(*informergenargs.CustomArgs) + if !ok { + glog.Fatalf("Wrong CustomArgs type: %T", arguments.CustomArgs) + } + + internalVersionPackagePath := filepath.Join(arguments.OutputPackagePath) + externalVersionPackagePath := filepath.Join(arguments.OutputPackagePath) + if !customArgs.SingleDirectory { + internalVersionPackagePath = filepath.Join(arguments.OutputPackagePath, "internalversion") + externalVersionPackagePath = filepath.Join(arguments.OutputPackagePath, "externalversions") + } + + var packageList generator.Packages + typesForGroupVersion := make(map[clientgentypes.GroupVersion][]*types.Type) + + externalGroupVersions := make(map[string]clientgentypes.GroupVersions) + internalGroupVersions := make(map[string]clientgentypes.GroupVersions) + groupGoNames := make(map[string]string) + for _, inputDir := range arguments.InputDirs { + p := context.Universe.Package(vendorless(inputDir)) + + objectMeta, internal, err := objectMetaForPackage(p) + if err != nil { + glog.Fatal(err) + } + if objectMeta == nil { + // no types in this package had genclient + continue + } + + var gv clientgentypes.GroupVersion + var targetGroupVersions map[string]clientgentypes.GroupVersions + + if internal { + lastSlash := strings.LastIndex(p.Path, "/") + if lastSlash == -1 { + glog.Fatalf("error constructing internal group version for package %q", p.Path) + } + gv.Group = clientgentypes.Group(p.Path[lastSlash+1:]) + targetGroupVersions = internalGroupVersions + } else { + parts := strings.Split(p.Path, "/") + gv.Group = clientgentypes.Group(parts[len(parts)-2]) + gv.Version = clientgentypes.Version(parts[len(parts)-1]) + targetGroupVersions = externalGroupVersions + } + groupPackageName := gv.Group.NonEmpty() + gvPackage := path.Clean(p.Path) + + // If there's a comment of the form "// +groupName=somegroup" or + // "// +groupName=somegroup.foo.bar.io", use the first field (somegroup) as the name of the + // group when generating. + if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil { + gv.Group = clientgentypes.Group(override[0]) + } + + // If there's a comment of the form "// +groupGoName=SomeUniqueShortName", use that as + // the Go group identifier in CamelCase. It defaults + groupGoNames[groupPackageName] = namer.IC(strings.Split(gv.Group.NonEmpty(), ".")[0]) + if override := types.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil { + groupGoNames[groupPackageName] = namer.IC(override[0]) + } + + var typesToGenerate []*types.Type + for _, t := range p.Types { + tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + if !tags.GenerateClient || tags.NoVerbs || !tags.HasVerb("list") || !tags.HasVerb("watch") { + continue + } + + typesToGenerate = append(typesToGenerate, t) + + if _, ok := typesForGroupVersion[gv]; !ok { + typesForGroupVersion[gv] = []*types.Type{} + } + typesForGroupVersion[gv] = append(typesForGroupVersion[gv], t) + } + if len(typesToGenerate) == 0 { + continue + } + + groupVersionsEntry, ok := targetGroupVersions[groupPackageName] + if !ok { + groupVersionsEntry = clientgentypes.GroupVersions{ + PackageName: groupPackageName, + Group: gv.Group, + } + } + groupVersionsEntry.Versions = append(groupVersionsEntry.Versions, clientgentypes.PackageVersion{Version: gv.Version, Package: gvPackage}) + targetGroupVersions[groupPackageName] = groupVersionsEntry + + orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)} + typesToGenerate = orderer.OrderTypes(typesToGenerate) + + if internal { + packageList = append(packageList, versionPackage(internalVersionPackagePath, groupPackageName, gv, groupGoNames[groupPackageName], boilerplate, typesToGenerate, customArgs.InternalClientSetPackage, customArgs.ListersPackage)) + } else { + packageList = append(packageList, versionPackage(externalVersionPackagePath, groupPackageName, gv, groupGoNames[groupPackageName], boilerplate, typesToGenerate, customArgs.VersionedClientSetPackage, customArgs.ListersPackage)) + } + } + + if len(externalGroupVersions) != 0 { + packageList = append(packageList, factoryInterfacePackage(externalVersionPackagePath, boilerplate, customArgs.VersionedClientSetPackage)) + packageList = append(packageList, factoryPackage(externalVersionPackagePath, boilerplate, groupGoNames, externalGroupVersions, customArgs.VersionedClientSetPackage, typesForGroupVersion)) + for _, gvs := range externalGroupVersions { + packageList = append(packageList, groupPackage(externalVersionPackagePath, gvs, boilerplate)) + } + } + + if len(internalGroupVersions) != 0 { + packageList = append(packageList, factoryInterfacePackage(internalVersionPackagePath, boilerplate, customArgs.InternalClientSetPackage)) + packageList = append(packageList, factoryPackage(internalVersionPackagePath, boilerplate, groupGoNames, internalGroupVersions, customArgs.InternalClientSetPackage, typesForGroupVersion)) + for _, gvs := range internalGroupVersions { + packageList = append(packageList, groupPackage(internalVersionPackagePath, gvs, boilerplate)) + } + } + + return packageList +} + +func factoryPackage(basePackage string, boilerplate []byte, groupGoNames map[string]string, groupVersions map[string]clientgentypes.GroupVersions, clientSetPackage string, typesForGroupVersion map[clientgentypes.GroupVersion][]*types.Type) generator.Package { + return &generator.DefaultPackage{ + PackageName: filepath.Base(basePackage), + PackagePath: basePackage, + HeaderText: boilerplate, + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = append(generators, &factoryGenerator{ + DefaultGen: generator.DefaultGen{ + OptionalName: "factory", + }, + outputPackage: basePackage, + imports: generator.NewImportTracker(), + groupVersions: groupVersions, + clientSetPackage: clientSetPackage, + internalInterfacesPackage: packageForInternalInterfaces(basePackage), + gvGoNames: groupGoNames, + }) + + generators = append(generators, &genericGenerator{ + DefaultGen: generator.DefaultGen{ + OptionalName: "generic", + }, + outputPackage: basePackage, + imports: generator.NewImportTracker(), + groupVersions: groupVersions, + typesForGroupVersion: typesForGroupVersion, + groupGoNames: groupGoNames, + }) + + return generators + }, + } +} + +func factoryInterfacePackage(basePackage string, boilerplate []byte, clientSetPackage string) generator.Package { + packagePath := packageForInternalInterfaces(basePackage) + + return &generator.DefaultPackage{ + PackageName: filepath.Base(packagePath), + PackagePath: packagePath, + HeaderText: boilerplate, + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = append(generators, &factoryInterfaceGenerator{ + DefaultGen: generator.DefaultGen{ + OptionalName: "factory_interfaces", + }, + outputPackage: packagePath, + imports: generator.NewImportTracker(), + clientSetPackage: clientSetPackage, + }) + + return generators + }, + } +} + +func groupPackage(basePackage string, groupVersions clientgentypes.GroupVersions, boilerplate []byte) generator.Package { + packagePath := filepath.Join(basePackage, groupVersions.PackageName) + groupPkgName := strings.Split(string(groupVersions.Group), ".")[0] + + return &generator.DefaultPackage{ + PackageName: groupPkgName, + PackagePath: packagePath, + HeaderText: boilerplate, + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = append(generators, &groupInterfaceGenerator{ + DefaultGen: generator.DefaultGen{ + OptionalName: "interface", + }, + outputPackage: packagePath, + groupVersions: groupVersions, + imports: generator.NewImportTracker(), + internalInterfacesPackage: packageForInternalInterfaces(basePackage), + }) + return generators + }, + FilterFunc: func(c *generator.Context, t *types.Type) bool { + tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + return tags.GenerateClient && tags.HasVerb("list") && tags.HasVerb("watch") + }, + } +} + +func versionPackage(basePackage string, groupPkgName string, gv clientgentypes.GroupVersion, groupGoName string, boilerplate []byte, typesToGenerate []*types.Type, clientSetPackage, listersPackage string) generator.Package { + packagePath := filepath.Join(basePackage, groupPkgName, strings.ToLower(gv.Version.NonEmpty())) + + return &generator.DefaultPackage{ + PackageName: strings.ToLower(gv.Version.NonEmpty()), + PackagePath: packagePath, + HeaderText: boilerplate, + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = append(generators, &versionInterfaceGenerator{ + DefaultGen: generator.DefaultGen{ + OptionalName: "interface", + }, + outputPackage: packagePath, + imports: generator.NewImportTracker(), + types: typesToGenerate, + internalInterfacesPackage: packageForInternalInterfaces(basePackage), + }) + + for _, t := range typesToGenerate { + generators = append(generators, &informerGenerator{ + DefaultGen: generator.DefaultGen{ + OptionalName: strings.ToLower(t.Name.Name), + }, + outputPackage: packagePath, + groupPkgName: groupPkgName, + groupVersion: gv, + groupGoName: groupGoName, + typeToGenerate: t, + imports: generator.NewImportTracker(), + clientSetPackage: clientSetPackage, + listersPackage: listersPackage, + internalInterfacesPackage: packageForInternalInterfaces(basePackage), + }) + } + return generators + }, + FilterFunc: func(c *generator.Context, t *types.Type) bool { + tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + return tags.GenerateClient && tags.HasVerb("list") && tags.HasVerb("watch") + }, + } +} diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/generators/tags.go b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/tags.go new file mode 100644 index 0000000000..afa2878152 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/tags.go @@ -0,0 +1,33 @@ +/* +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 generators + +import ( + "github.com/golang/glog" + "k8s.io/gengo/types" +) + +// extractBoolTagOrDie gets the comment-tags for the key and asserts that, if +// it exists, the value is boolean. If the tag did not exist, it returns +// false. +func extractBoolTagOrDie(key string, lines []string) bool { + val, err := types.ExtractSingleBoolCommentTag("+", key, false, lines) + if err != nil { + glog.Fatal(err) + } + return val +} diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/generators/types.go b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/types.go new file mode 100644 index 0000000000..27d4bd51ab --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/types.go @@ -0,0 +1,42 @@ +/* +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 generators + +import "k8s.io/gengo/types" + +var ( + apiScheme = types.Name{Package: "k8s.io/kubernetes/pkg/api/legacyscheme", Name: "Scheme"} + cacheGenericLister = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "GenericLister"} + cacheIndexers = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "Indexers"} + cacheListWatch = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "ListWatch"} + cacheMetaNamespaceIndexFunc = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "MetaNamespaceIndexFunc"} + cacheNamespaceIndex = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "NamespaceIndex"} + cacheNewGenericLister = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "NewGenericLister"} + cacheNewSharedIndexInformer = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "NewSharedIndexInformer"} + cacheSharedIndexInformer = types.Name{Package: "k8s.io/client-go/tools/cache", Name: "SharedIndexInformer"} + listOptions = types.Name{Package: "k8s.io/kubernetes/pkg/apis/core", Name: "ListOptions"} + reflectType = types.Name{Package: "reflect", Name: "Type"} + runtimeObject = types.Name{Package: "k8s.io/apimachinery/pkg/runtime", Name: "Object"} + schemaGroupResource = types.Name{Package: "k8s.io/apimachinery/pkg/runtime/schema", Name: "GroupResource"} + schemaGroupVersionResource = types.Name{Package: "k8s.io/apimachinery/pkg/runtime/schema", Name: "GroupVersionResource"} + syncMutex = types.Name{Package: "sync", Name: "Mutex"} + timeDuration = types.Name{Package: "time", Name: "Duration"} + v1ListOptions = types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ListOptions"} + metav1NamespaceAll = types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "NamespaceAll"} + metav1Object = types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "Object"} + watchInterface = types.Name{Package: "k8s.io/apimachinery/pkg/watch", Name: "Interface"} +) diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/generators/versioninterface.go b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/versioninterface.go new file mode 100644 index 0000000000..1cd27d5cdd --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/generators/versioninterface.go @@ -0,0 +1,109 @@ +/* +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 generators + +import ( + "io" + + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/code-generator/cmd/client-gen/generators/util" +) + +// versionInterfaceGenerator generates the per-version interface file. +type versionInterfaceGenerator struct { + generator.DefaultGen + outputPackage string + imports namer.ImportTracker + types []*types.Type + filtered bool + internalInterfacesPackage string +} + +var _ generator.Generator = &versionInterfaceGenerator{} + +func (g *versionInterfaceGenerator) Filter(c *generator.Context, t *types.Type) bool { + if !g.filtered { + g.filtered = true + return true + } + return false +} + +func (g *versionInterfaceGenerator) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *versionInterfaceGenerator) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.imports.ImportLines()...) + return +} + +func (g *versionInterfaceGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + + m := map[string]interface{}{ + "interfacesTweakListOptionsFunc": c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "TweakListOptionsFunc"}), + "interfacesSharedInformerFactory": c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "SharedInformerFactory"}), + "types": g.types, + } + + sw.Do(versionTemplate, m) + for _, typeDef := range g.types { + tags, err := util.ParseClientGenTags(typeDef.SecondClosestCommentLines) + if err != nil { + return err + } + m["namespaced"] = !tags.NonNamespaced + m["type"] = typeDef + sw.Do(versionFuncTemplate, m) + } + + return sw.Error() +} + +var versionTemplate = ` +// Interface provides access to all the informers in this group version. +type Interface interface { + $range .types -$ + // $.|publicPlural$ returns a $.|public$Informer. + $.|publicPlural$() $.|public$Informer + $end$ +} + +type version struct { + factory $.interfacesSharedInformerFactory|raw$ + namespace string + tweakListOptions $.interfacesTweakListOptionsFunc|raw$ +} + +// New returns a new Interface. +func New(f $.interfacesSharedInformerFactory|raw$, namespace string, tweakListOptions $.interfacesTweakListOptionsFunc|raw$) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} +` + +var versionFuncTemplate = ` +// $.type|publicPlural$ returns a $.type|public$Informer. +func (v *version) $.type|publicPlural$() $.type|public$Informer { + return &$.type|private$Informer{factory: v.factory$if .namespaced$, namespace: v.namespace$end$, tweakListOptions: v.tweakListOptions} +} +` diff --git a/vendor/k8s.io/code-generator/cmd/informer-gen/main.go b/vendor/k8s.io/code-generator/cmd/informer-gen/main.go new file mode 100644 index 0000000000..bfe826080c --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/informer-gen/main.go @@ -0,0 +1,62 @@ +/* +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 main + +import ( + "flag" + "path/filepath" + + "github.com/golang/glog" + "github.com/spf13/pflag" + "k8s.io/code-generator/cmd/informer-gen/generators" + "k8s.io/code-generator/pkg/util" + "k8s.io/gengo/args" + + generatorargs "k8s.io/code-generator/cmd/informer-gen/args" +) + +func main() { + genericArgs, customArgs := generatorargs.NewDefaults() + + // Override defaults. + // TODO: move out of informer-gen + genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath()) + genericArgs.OutputPackagePath = "k8s.io/kubernetes/pkg/client/informers/informers_generated" + customArgs.VersionedClientSetPackage = "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" + customArgs.InternalClientSetPackage = "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + customArgs.ListersPackage = "k8s.io/kubernetes/pkg/client/listers" + + genericArgs.AddFlags(pflag.CommandLine) + customArgs.AddFlags(pflag.CommandLine) + flag.Set("logtostderr", "true") + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() + + if err := generatorargs.Validate(genericArgs); err != nil { + glog.Fatalf("Error: %v", err) + } + + // Run it. + if err := genericArgs.Execute( + generators.NameSystems(), + generators.DefaultNameSystem(), + generators.Packages, + ); err != nil { + glog.Fatalf("Error: %v", err) + } + glog.V(2).Info("Completed successfully.") +} diff --git a/vendor/k8s.io/code-generator/cmd/lister-gen/.import-restrictions b/vendor/k8s.io/code-generator/cmd/lister-gen/.import-restrictions new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/lister-gen/.import-restrictions @@ -0,0 +1 @@ +{} diff --git a/vendor/k8s.io/code-generator/cmd/lister-gen/args/args.go b/vendor/k8s.io/code-generator/cmd/lister-gen/args/args.go new file mode 100644 index 0000000000..34914ea8c9 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/lister-gen/args/args.go @@ -0,0 +1,56 @@ +/* +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 args + +import ( + "fmt" + "path" + + "github.com/spf13/pflag" + codegenutil "k8s.io/code-generator/pkg/util" + "k8s.io/gengo/args" +) + +// CustomArgs is used by the gengo framework to pass args specific to this generator. +type CustomArgs struct{} + +// NewDefaults returns default arguments for the generator. +func NewDefaults() (*args.GeneratorArgs, *CustomArgs) { + genericArgs := args.Default().WithoutDefaultFlagParsing() + customArgs := &CustomArgs{} + genericArgs.CustomArgs = customArgs + + if pkg := codegenutil.CurrentPackage(); len(pkg) != 0 { + genericArgs.OutputPackagePath = path.Join(pkg, "pkg/client/listers") + } + + return genericArgs, customArgs +} + +// AddFlags add the generator flags to the flag set. +func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) {} + +// Validate checks the given arguments. +func Validate(genericArgs *args.GeneratorArgs) error { + _ = genericArgs.CustomArgs.(*CustomArgs) + + if len(genericArgs.OutputPackagePath) == 0 { + return fmt.Errorf("output package cannot be empty") + } + + return nil +} diff --git a/vendor/k8s.io/code-generator/cmd/lister-gen/generators/expansion.go b/vendor/k8s.io/code-generator/cmd/lister-gen/generators/expansion.go new file mode 100644 index 0000000000..dd45d7749c --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/lister-gen/generators/expansion.go @@ -0,0 +1,67 @@ +/* +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 generators + +import ( + "io" + "os" + "path/filepath" + "strings" + + "k8s.io/gengo/generator" + "k8s.io/gengo/types" + + "k8s.io/code-generator/cmd/client-gen/generators/util" +) + +// expansionGenerator produces a file for a expansion interfaces. +type expansionGenerator struct { + generator.DefaultGen + packagePath string + types []*types.Type +} + +// We only want to call GenerateType() once per group. +func (g *expansionGenerator) Filter(c *generator.Context, t *types.Type) bool { + return t == g.types[0] +} + +func (g *expansionGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + for _, t := range g.types { + tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + if _, err := os.Stat(filepath.Join(g.packagePath, strings.ToLower(t.Name.Name+"_expansion.go"))); os.IsNotExist(err) { + sw.Do(expansionInterfaceTemplate, t) + if !tags.NonNamespaced { + sw.Do(namespacedExpansionInterfaceTemplate, t) + } + } + } + return sw.Error() +} + +var expansionInterfaceTemplate = ` +// $.|public$ListerExpansion allows custom methods to be added to +// $.|public$Lister. +type $.|public$ListerExpansion interface {} +` + +var namespacedExpansionInterfaceTemplate = ` +// $.|public$NamespaceListerExpansion allows custom methods to be added to +// $.|public$NamespaceLister. +type $.|public$NamespaceListerExpansion interface {} +` diff --git a/vendor/k8s.io/code-generator/cmd/lister-gen/generators/lister.go b/vendor/k8s.io/code-generator/cmd/lister-gen/generators/lister.go new file mode 100644 index 0000000000..cde6e2f770 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/lister-gen/generators/lister.go @@ -0,0 +1,371 @@ +/* +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 generators + +import ( + "fmt" + "io" + "path/filepath" + "strings" + + "k8s.io/gengo/args" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/code-generator/cmd/client-gen/generators/util" + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + + "github.com/golang/glog" +) + +// NameSystems returns the name system used by the generators in this package. +func NameSystems() namer.NameSystems { + pluralExceptions := map[string]string{ + "Endpoints": "Endpoints", + } + return namer.NameSystems{ + "public": namer.NewPublicNamer(0), + "private": namer.NewPrivateNamer(0), + "raw": namer.NewRawNamer("", nil), + "publicPlural": namer.NewPublicPluralNamer(pluralExceptions), + "allLowercasePlural": namer.NewAllLowercasePluralNamer(pluralExceptions), + "lowercaseSingular": &lowercaseSingularNamer{}, + } +} + +// lowercaseSingularNamer implements Namer +type lowercaseSingularNamer struct{} + +// Name returns t's name in all lowercase. +func (n *lowercaseSingularNamer) Name(t *types.Type) string { + return strings.ToLower(t.Name.Name) +} + +// DefaultNameSystem returns the default name system for ordering the types to be +// processed by the generators in this package. +func DefaultNameSystem() string { + return "public" +} + +// Packages makes the client package definition. +func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { + boilerplate, err := arguments.LoadGoBoilerplate() + if err != nil { + glog.Fatalf("Failed loading boilerplate: %v", err) + } + + var packageList generator.Packages + for _, inputDir := range arguments.InputDirs { + p := context.Universe.Package(inputDir) + + objectMeta, internal, err := objectMetaForPackage(p) + if err != nil { + glog.Fatal(err) + } + if objectMeta == nil { + // no types in this package had genclient + continue + } + + var gv clientgentypes.GroupVersion + var internalGVPkg string + + if internal { + lastSlash := strings.LastIndex(p.Path, "/") + if lastSlash == -1 { + glog.Fatalf("error constructing internal group version for package %q", p.Path) + } + gv.Group = clientgentypes.Group(p.Path[lastSlash+1:]) + internalGVPkg = p.Path + } else { + parts := strings.Split(p.Path, "/") + gv.Group = clientgentypes.Group(parts[len(parts)-2]) + gv.Version = clientgentypes.Version(parts[len(parts)-1]) + + internalGVPkg = strings.Join(parts[0:len(parts)-1], "/") + } + groupPackageName := strings.ToLower(gv.Group.NonEmpty()) + + // If there's a comment of the form "// +groupName=somegroup" or + // "// +groupName=somegroup.foo.bar.io", use the first field (somegroup) as the name of the + // group when generating. + if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil { + gv.Group = clientgentypes.Group(strings.SplitN(override[0], ".", 2)[0]) + } + + var typesToGenerate []*types.Type + for _, t := range p.Types { + tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + if !tags.GenerateClient || !tags.HasVerb("list") || !tags.HasVerb("get") { + continue + } + typesToGenerate = append(typesToGenerate, t) + } + if len(typesToGenerate) == 0 { + continue + } + orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)} + typesToGenerate = orderer.OrderTypes(typesToGenerate) + + packagePath := filepath.Join(arguments.OutputPackagePath, groupPackageName, strings.ToLower(gv.Version.NonEmpty())) + packageList = append(packageList, &generator.DefaultPackage{ + PackageName: strings.ToLower(gv.Version.NonEmpty()), + PackagePath: packagePath, + HeaderText: boilerplate, + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = append(generators, &expansionGenerator{ + DefaultGen: generator.DefaultGen{ + OptionalName: "expansion_generated", + }, + packagePath: filepath.Join(arguments.OutputBase, packagePath), + types: typesToGenerate, + }) + + for _, t := range typesToGenerate { + generators = append(generators, &listerGenerator{ + DefaultGen: generator.DefaultGen{ + OptionalName: strings.ToLower(t.Name.Name), + }, + outputPackage: arguments.OutputPackagePath, + groupVersion: gv, + internalGVPkg: internalGVPkg, + typeToGenerate: t, + imports: generator.NewImportTracker(), + objectMeta: objectMeta, + }) + } + return generators + }, + FilterFunc: func(c *generator.Context, t *types.Type) bool { + tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + return tags.GenerateClient && tags.HasVerb("list") && tags.HasVerb("get") + }, + }) + } + + return packageList +} + +// objectMetaForPackage returns the type of ObjectMeta used by package p. +func objectMetaForPackage(p *types.Package) (*types.Type, bool, error) { + generatingForPackage := false + for _, t := range p.Types { + // filter out types which dont have genclient. + if !util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)).GenerateClient { + continue + } + generatingForPackage = true + for _, member := range t.Members { + if member.Name == "ObjectMeta" { + return member.Type, isInternal(member), nil + } + } + } + if generatingForPackage { + return nil, false, fmt.Errorf("unable to find ObjectMeta for any types in package %s", p.Path) + } + return nil, false, nil +} + +// isInternal returns true if the tags for a member do not contain a json tag +func isInternal(m types.Member) bool { + return !strings.Contains(m.Tags, "json") +} + +// listerGenerator produces a file of listers for a given GroupVersion and +// type. +type listerGenerator struct { + generator.DefaultGen + outputPackage string + groupVersion clientgentypes.GroupVersion + internalGVPkg string + typeToGenerate *types.Type + imports namer.ImportTracker + objectMeta *types.Type +} + +var _ generator.Generator = &listerGenerator{} + +func (g *listerGenerator) Filter(c *generator.Context, t *types.Type) bool { + return t == g.typeToGenerate +} + +func (g *listerGenerator) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *listerGenerator) Imports(c *generator.Context) (imports []string) { + imports = append(imports, g.imports.ImportLines()...) + imports = append(imports, "k8s.io/apimachinery/pkg/api/errors") + imports = append(imports, "k8s.io/apimachinery/pkg/labels") + // for Indexer + imports = append(imports, "k8s.io/client-go/tools/cache") + return +} + +func (g *listerGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + + glog.V(5).Infof("processing type %v", t) + m := map[string]interface{}{ + "Resource": c.Universe.Function(types.Name{Package: t.Name.Package, Name: "Resource"}), + "type": t, + "objectMeta": g.objectMeta, + } + + tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) + if err != nil { + return err + } + + if tags.NonNamespaced { + sw.Do(typeListerInterface_NonNamespaced, m) + } else { + sw.Do(typeListerInterface, m) + } + + sw.Do(typeListerStruct, m) + sw.Do(typeListerConstructor, m) + sw.Do(typeLister_List, m) + + if tags.NonNamespaced { + sw.Do(typeLister_NonNamespacedGet, m) + return sw.Error() + } + + sw.Do(typeLister_NamespaceLister, m) + sw.Do(namespaceListerInterface, m) + sw.Do(namespaceListerStruct, m) + sw.Do(namespaceLister_List, m) + sw.Do(namespaceLister_Get, m) + + return sw.Error() +} + +var typeListerInterface = ` +// $.type|public$Lister helps list $.type|publicPlural$. +type $.type|public$Lister interface { + // List lists all $.type|publicPlural$ in the indexer. + List(selector labels.Selector) (ret []*$.type|raw$, err error) + // $.type|publicPlural$ returns an object that can list and get $.type|publicPlural$. + $.type|publicPlural$(namespace string) $.type|public$NamespaceLister + $.type|public$ListerExpansion +} +` + +var typeListerInterface_NonNamespaced = ` +// $.type|public$Lister helps list $.type|publicPlural$. +type $.type|public$Lister interface { + // List lists all $.type|publicPlural$ in the indexer. + List(selector labels.Selector) (ret []*$.type|raw$, err error) + // Get retrieves the $.type|public$ from the index for a given name. + Get(name string) (*$.type|raw$, error) + $.type|public$ListerExpansion +} +` + +var typeListerStruct = ` +// $.type|private$Lister implements the $.type|public$Lister interface. +type $.type|private$Lister struct { + indexer cache.Indexer +} +` + +var typeListerConstructor = ` +// New$.type|public$Lister returns a new $.type|public$Lister. +func New$.type|public$Lister(indexer cache.Indexer) $.type|public$Lister { + return &$.type|private$Lister{indexer: indexer} +} +` + +var typeLister_List = ` +// List lists all $.type|publicPlural$ in the indexer. +func (s *$.type|private$Lister) List(selector labels.Selector) (ret []*$.type|raw$, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*$.type|raw$)) + }) + return ret, err +} +` + +var typeLister_NamespaceLister = ` +// $.type|publicPlural$ returns an object that can list and get $.type|publicPlural$. +func (s *$.type|private$Lister) $.type|publicPlural$(namespace string) $.type|public$NamespaceLister { + return $.type|private$NamespaceLister{indexer: s.indexer, namespace: namespace} +} +` + +var typeLister_NonNamespacedGet = ` +// Get retrieves the $.type|public$ from the index for a given name. +func (s *$.type|private$Lister) Get(name string) (*$.type|raw$, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound($.Resource|raw$("$.type|lowercaseSingular$"), name) + } + return obj.(*$.type|raw$), nil +} +` + +var namespaceListerInterface = ` +// $.type|public$NamespaceLister helps list and get $.type|publicPlural$. +type $.type|public$NamespaceLister interface { + // List lists all $.type|publicPlural$ in the indexer for a given namespace. + List(selector labels.Selector) (ret []*$.type|raw$, err error) + // Get retrieves the $.type|public$ from the indexer for a given namespace and name. + Get(name string) (*$.type|raw$, error) + $.type|public$NamespaceListerExpansion +} +` + +var namespaceListerStruct = ` +// $.type|private$NamespaceLister implements the $.type|public$NamespaceLister +// interface. +type $.type|private$NamespaceLister struct { + indexer cache.Indexer + namespace string +} +` + +var namespaceLister_List = ` +// List lists all $.type|publicPlural$ in the indexer for a given namespace. +func (s $.type|private$NamespaceLister) List(selector labels.Selector) (ret []*$.type|raw$, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*$.type|raw$)) + }) + return ret, err +} +` + +var namespaceLister_Get = ` +// Get retrieves the $.type|public$ from the indexer for a given namespace and name. +func (s $.type|private$NamespaceLister) Get(name string) (*$.type|raw$, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound($.Resource|raw$("$.type|lowercaseSingular$"), name) + } + return obj.(*$.type|raw$), nil +} +` diff --git a/vendor/k8s.io/code-generator/cmd/lister-gen/generators/tags.go b/vendor/k8s.io/code-generator/cmd/lister-gen/generators/tags.go new file mode 100644 index 0000000000..afa2878152 --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/lister-gen/generators/tags.go @@ -0,0 +1,33 @@ +/* +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 generators + +import ( + "github.com/golang/glog" + "k8s.io/gengo/types" +) + +// extractBoolTagOrDie gets the comment-tags for the key and asserts that, if +// it exists, the value is boolean. If the tag did not exist, it returns +// false. +func extractBoolTagOrDie(key string, lines []string) bool { + val, err := types.ExtractSingleBoolCommentTag("+", key, false, lines) + if err != nil { + glog.Fatal(err) + } + return val +} diff --git a/vendor/k8s.io/code-generator/cmd/lister-gen/main.go b/vendor/k8s.io/code-generator/cmd/lister-gen/main.go new file mode 100644 index 0000000000..d5ff8e46ee --- /dev/null +++ b/vendor/k8s.io/code-generator/cmd/lister-gen/main.go @@ -0,0 +1,59 @@ +/* +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 main + +import ( + "flag" + "path/filepath" + + "github.com/golang/glog" + "github.com/spf13/pflag" + "k8s.io/code-generator/cmd/lister-gen/generators" + "k8s.io/code-generator/pkg/util" + "k8s.io/gengo/args" + + generatorargs "k8s.io/code-generator/cmd/lister-gen/args" +) + +func main() { + genericArgs, customArgs := generatorargs.NewDefaults() + + // Override defaults. + // TODO: move this out of lister-gen + genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath()) + genericArgs.OutputPackagePath = "k8s.io/kubernetes/pkg/client/listers" + + genericArgs.AddFlags(pflag.CommandLine) + customArgs.AddFlags(pflag.CommandLine) + flag.Set("logtostderr", "true") + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() + + if err := generatorargs.Validate(genericArgs); err != nil { + glog.Fatalf("Error: %v", err) + } + + // Run it. + if err := genericArgs.Execute( + generators.NameSystems(), + generators.DefaultNameSystem(), + generators.Packages, + ); err != nil { + glog.Fatalf("Error: %v", err) + } + glog.V(2).Info("Completed successfully.") +} diff --git a/vendor/k8s.io/code-generator/generate-groups.sh b/vendor/k8s.io/code-generator/generate-groups.sh new file mode 100755 index 0000000000..d7ad5b2e07 --- /dev/null +++ b/vendor/k8s.io/code-generator/generate-groups.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +# 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. + +set -o errexit +set -o nounset +set -o pipefail + +# generate-groups generates everything for a project with external types only, e.g. a project based +# on CustomResourceDefinitions. + +if [ "$#" -lt 4 ] || [ "${1}" == "--help" ]; then + cat < ... + + the generators comma separated to run (deepcopy,defaulter,client,lister,informer) or "all". + the output package name (e.g. github.com/example/project/pkg/generated). + the external types dir (e.g. github.com/example/api or github.com/example/project/pkg/apis). + the groups and their versions in the format "groupA:v1,v2 groupB:v1 groupC:v2", relative + to . + ... arbitrary flags passed to all generator binaries. + + +Examples: + $(basename $0) all github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1" + $(basename $0) deepcopy,client github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1" +EOF + exit 0 +fi + +GENS="$1" +OUTPUT_PKG="$2" +APIS_PKG="$3" +GROUPS_WITH_VERSIONS="$4" +shift 4 + +( + # To support running this script from anywhere, we have to first cd into this directory + # so we can install the tools. + cd $(dirname "${0}") + go install ./cmd/{defaulter-gen,client-gen,lister-gen,informer-gen,deepcopy-gen} +) + +function codegen::join() { local IFS="$1"; shift; echo "$*"; } + +# enumerate group versions +FQ_APIS=() # e.g. k8s.io/api/apps/v1 +for GVs in ${GROUPS_WITH_VERSIONS}; do + IFS=: read G Vs <<<"${GVs}" + + # enumerate versions + for V in ${Vs//,/ }; do + FQ_APIS+=(${APIS_PKG}/${G}/${V}) + done +done + +if [ "${GENS}" = "all" ] || grep -qw "deepcopy" <<<"${GENS}"; then + echo "Generating deepcopy funcs" + ${GOPATH}/bin/deepcopy-gen --input-dirs $(codegen::join , "${FQ_APIS[@]}") -O zz_generated.deepcopy --bounding-dirs ${APIS_PKG} "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "client" <<<"${GENS}"; then + echo "Generating clientset for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/clientset" + ${GOPATH}/bin/client-gen --clientset-name ${CLIENTSET_NAME_VERSIONED:-versioned} --input-base "" --input $(codegen::join , "${FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/clientset "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "lister" <<<"${GENS}"; then + echo "Generating listers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/listers" + ${GOPATH}/bin/lister-gen --input-dirs $(codegen::join , "${FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/listers "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "informer" <<<"${GENS}"; then + echo "Generating informers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/informers" + ${GOPATH}/bin/informer-gen \ + --input-dirs $(codegen::join , "${FQ_APIS[@]}") \ + --versioned-clientset-package ${OUTPUT_PKG}/clientset/${CLIENTSET_NAME_VERSIONED:-versioned} \ + --listers-package ${OUTPUT_PKG}/listers \ + --output-package ${OUTPUT_PKG}/informers \ + "$@" +fi diff --git a/vendor/k8s.io/code-generator/pkg/util/build.go b/vendor/k8s.io/code-generator/pkg/util/build.go new file mode 100644 index 0000000000..6ea8f52ee0 --- /dev/null +++ b/vendor/k8s.io/code-generator/pkg/util/build.go @@ -0,0 +1,61 @@ +/* +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 util + +import ( + gobuild "go/build" + "path" + "path/filepath" + "reflect" + "strings" +) + +type empty struct{} + +// CurrentPackage returns the go package of the current directory, or "" if it cannot +// be derived from the GOPATH. +func CurrentPackage() string { + for _, root := range gobuild.Default.SrcDirs() { + if pkg, ok := hasSubdir(root, "."); ok { + return pkg + } + } + return "" +} + +func hasSubdir(root, dir string) (rel string, ok bool) { + // ensure a tailing separator to properly compare on word-boundaries + const sep = string(filepath.Separator) + root = filepath.Clean(root) + if !strings.HasSuffix(root, sep) { + root += sep + } + + // check whether root dir starts with root + dir = filepath.Clean(dir) + if !strings.HasPrefix(dir, root) { + return "", false + } + + // cut off root + return filepath.ToSlash(dir[len(root):]), true +} + +// BoilerplatePath uses the boilerplate in code-generator by calculating the relative path to it. +func BoilerplatePath() string { + return path.Join(reflect.TypeOf(empty{}).PkgPath(), "/../../hack/boilerplate.go.txt") +} diff --git a/vendor/k8s.io/gengo/LICENSE b/vendor/k8s.io/gengo/LICENSE new file mode 100644 index 0000000000..00b2401109 --- /dev/null +++ b/vendor/k8s.io/gengo/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/vendor/k8s.io/gengo/args/args.go b/vendor/k8s.io/gengo/args/args.go new file mode 100644 index 0000000000..2f8680d1e0 --- /dev/null +++ b/vendor/k8s.io/gengo/args/args.go @@ -0,0 +1,199 @@ +/* +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 args has common command-line flags for generation programs. +package args + +import ( + "bytes" + goflag "flag" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "strconv" + "strings" + "time" + + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/parser" + "k8s.io/gengo/types" + + "github.com/spf13/pflag" +) + +// Default returns a defaulted GeneratorArgs. You may change the defaults +// before calling AddFlags. +func Default() *GeneratorArgs { + return &GeneratorArgs{ + OutputBase: DefaultSourceTree(), + GoHeaderFilePath: filepath.Join(DefaultSourceTree(), "k8s.io/gengo/boilerplate/boilerplate.go.txt"), + GeneratedBuildTag: "ignore_autogenerated", + GeneratedByCommentTemplate: "// Code generated by GENERATOR_NAME. DO NOT EDIT.", + defaultCommandLineFlags: true, + } +} + +// GeneratorArgs has arguments that are passed to generators. +type GeneratorArgs struct { + // Which directories to parse. + InputDirs []string + + // Source tree to write results to. + OutputBase string + + // Package path within the source tree. + OutputPackagePath string + + // Output file name. + OutputFileBaseName string + + // Where to get copyright header text. + GoHeaderFilePath string + + // If GeneratedByCommentTemplate is set, generate a "Code generated by" comment + // below the bloilerplate, of the format defined by this string. + // Any instances of "GENERATOR_NAME" will be replaced with the name of the code generator. + GeneratedByCommentTemplate string + + // If true, only verify, don't write anything. + VerifyOnly bool + + // GeneratedBuildTag is the tag used to identify code generated by execution + // of this type. Each generator should use a different tag, and different + // groups of generators (external API that depends on Kube generations) should + // keep tags distinct as well. + GeneratedBuildTag string + + // Any custom arguments go here + CustomArgs interface{} + + // Whether to use default command line flags + defaultCommandLineFlags bool +} + +// WithoutDefaultFlagParsing disables implicit addition of command line flags and parsing. +func (g *GeneratorArgs) WithoutDefaultFlagParsing() *GeneratorArgs { + g.defaultCommandLineFlags = false + return g +} + +func (g *GeneratorArgs) AddFlags(fs *pflag.FlagSet) { + fs.StringSliceVarP(&g.InputDirs, "input-dirs", "i", g.InputDirs, "Comma-separated list of import paths to get input types from.") + fs.StringVarP(&g.OutputBase, "output-base", "o", g.OutputBase, "Output base; defaults to $GOPATH/src/ or ./ if $GOPATH is not set.") + fs.StringVarP(&g.OutputPackagePath, "output-package", "p", g.OutputPackagePath, "Base package path.") + fs.StringVarP(&g.OutputFileBaseName, "output-file-base", "O", g.OutputFileBaseName, "Base name (without .go suffix) for output files.") + fs.StringVarP(&g.GoHeaderFilePath, "go-header-file", "h", g.GoHeaderFilePath, "File containing boilerplate header text. The string YEAR will be replaced with the current 4-digit year.") + fs.BoolVar(&g.VerifyOnly, "verify-only", g.VerifyOnly, "If true, only verify existing output, do not write anything.") + fs.StringVar(&g.GeneratedBuildTag, "build-tag", g.GeneratedBuildTag, "A Go build tag to use to identify files generated by this command. Should be unique.") +} + +// LoadGoBoilerplate loads the boilerplate file passed to --go-header-file. +func (g *GeneratorArgs) LoadGoBoilerplate() ([]byte, error) { + b, err := ioutil.ReadFile(g.GoHeaderFilePath) + if err != nil { + return nil, err + } + b = bytes.Replace(b, []byte("YEAR"), []byte(strconv.Itoa(time.Now().Year())), -1) + + if g.GeneratedByCommentTemplate != "" { + if len(b) != 0 { + b = append(b, byte('\n')) + } + generatorName := path.Base(os.Args[0]) + generatedByComment := strings.Replace(g.GeneratedByCommentTemplate, "GENERATOR_NAME", generatorName, -1) + s := fmt.Sprintf("%s\n\n", generatedByComment) + b = append(b, []byte(s)...) + } + return b, nil +} + +// NewBuilder makes a new parser.Builder and populates it with the input +// directories. +func (g *GeneratorArgs) NewBuilder() (*parser.Builder, error) { + b := parser.New() + // Ignore all auto-generated files. + b.AddBuildTags(g.GeneratedBuildTag) + + for _, d := range g.InputDirs { + var err error + if strings.HasSuffix(d, "/...") { + err = b.AddDirRecursive(strings.TrimSuffix(d, "/...")) + } else { + err = b.AddDir(d) + } + if err != nil { + return nil, fmt.Errorf("unable to add directory %q: %v", d, err) + } + } + return b, nil +} + +// InputIncludes returns true if the given package is a (sub) package of one of +// the InputDirs. +func (g *GeneratorArgs) InputIncludes(p *types.Package) bool { + for _, dir := range g.InputDirs { + d := dir + if strings.HasSuffix(d, "...") { + d = strings.TrimSuffix(d, "...") + } + if strings.HasPrefix(p.Path, d) { + return true + } + } + return false +} + +// DefaultSourceTree returns the /src directory of the first entry in $GOPATH. +// If $GOPATH is empty, it returns "./". Useful as a default output location. +func DefaultSourceTree() string { + paths := strings.Split(os.Getenv("GOPATH"), string(filepath.ListSeparator)) + if len(paths) > 0 && len(paths[0]) > 0 { + return filepath.Join(paths[0], "src") + } + return "./" +} + +// Execute implements main(). +// If you don't need any non-default behavior, use as: +// args.Default().Execute(...) +func (g *GeneratorArgs) Execute(nameSystems namer.NameSystems, defaultSystem string, pkgs func(*generator.Context, *GeneratorArgs) generator.Packages) error { + if g.defaultCommandLineFlags { + g.AddFlags(pflag.CommandLine) + pflag.CommandLine.AddGoFlagSet(goflag.CommandLine) + pflag.Parse() + } + + b, err := g.NewBuilder() + if err != nil { + return fmt.Errorf("Failed making a parser: %v", err) + } + + c, err := generator.NewContext(b, nameSystems, defaultSystem) + if err != nil { + return fmt.Errorf("Failed making a context: %v", err) + } + + c.Verify = g.VerifyOnly + packages := pkgs(c, g) + if err := c.ExecutePackages(g.OutputBase, packages); err != nil { + return fmt.Errorf("Failed executing generator: %v", err) + } + + return nil +} diff --git a/vendor/k8s.io/gengo/examples/deepcopy-gen/generators/deepcopy.go b/vendor/k8s.io/gengo/examples/deepcopy-gen/generators/deepcopy.go new file mode 100644 index 0000000000..40f1306d5d --- /dev/null +++ b/vendor/k8s.io/gengo/examples/deepcopy-gen/generators/deepcopy.go @@ -0,0 +1,924 @@ +/* +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 generators + +import ( + "fmt" + "io" + "path/filepath" + "sort" + "strings" + + "k8s.io/gengo/args" + "k8s.io/gengo/examples/set-gen/sets" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/klog" +) + +// CustomArgs is used tby the go2idl framework to pass args specific to this +// generator. +type CustomArgs struct { + BoundingDirs []string // Only deal with types rooted under these dirs. +} + +// This is the comment tag that carries parameters for deep-copy generation. +const ( + tagEnabledName = "k8s:deepcopy-gen" + interfacesTagName = tagEnabledName + ":interfaces" + interfacesNonPointerTagName = tagEnabledName + ":nonpointer-interfaces" // attach the DeepCopy methods to the +) + +// Known values for the comment tag. +const tagValuePackage = "package" + +// enabledTagValue holds parameters from a tagName tag. +type enabledTagValue struct { + value string + register bool +} + +func extractEnabledTypeTag(t *types.Type) *enabledTagValue { + comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...) + return extractEnabledTag(comments) +} + +func extractEnabledTag(comments []string) *enabledTagValue { + tagVals := types.ExtractCommentTags("+", comments)[tagEnabledName] + if tagVals == nil { + // No match for the tag. + return nil + } + // If there are multiple values, abort. + if len(tagVals) > 1 { + klog.Fatalf("Found %d %s tags: %q", len(tagVals), tagEnabledName, tagVals) + } + + // If we got here we are returning something. + tag := &enabledTagValue{} + + // Get the primary value. + parts := strings.Split(tagVals[0], ",") + if len(parts) >= 1 { + tag.value = parts[0] + } + + // Parse extra arguments. + parts = parts[1:] + for i := range parts { + kv := strings.SplitN(parts[i], "=", 2) + k := kv[0] + v := "" + if len(kv) == 2 { + v = kv[1] + } + switch k { + case "register": + if v != "false" { + tag.register = true + } + default: + klog.Fatalf("Unsupported %s param: %q", tagEnabledName, parts[i]) + } + } + return tag +} + +// TODO: This is created only to reduce number of changes in a single PR. +// Remove it and use PublicNamer instead. +func deepCopyNamer() *namer.NameStrategy { + return &namer.NameStrategy{ + Join: func(pre string, in []string, post string) string { + return strings.Join(in, "_") + }, + PrependPackageNames: 1, + } +} + +// NameSystems returns the name system used by the generators in this package. +func NameSystems() namer.NameSystems { + return namer.NameSystems{ + "public": deepCopyNamer(), + "raw": namer.NewRawNamer("", nil), + } +} + +// DefaultNameSystem returns the default name system for ordering the types to be +// processed by the generators in this package. +func DefaultNameSystem() string { + return "public" +} + +func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { + boilerplate, err := arguments.LoadGoBoilerplate() + if err != nil { + klog.Fatalf("Failed loading boilerplate: %v", err) + } + + inputs := sets.NewString(context.Inputs...) + packages := generator.Packages{} + header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...) + + boundingDirs := []string{} + if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok { + if customArgs.BoundingDirs == nil { + customArgs.BoundingDirs = context.Inputs + } + for i := range customArgs.BoundingDirs { + // Strip any trailing slashes - they are not exactly "correct" but + // this is friendlier. + boundingDirs = append(boundingDirs, strings.TrimRight(customArgs.BoundingDirs[i], "/")) + } + } + + for i := range inputs { + klog.V(5).Infof("Considering pkg %q", i) + pkg := context.Universe[i] + if pkg == nil { + // If the input had no Go files, for example. + continue + } + + ptag := extractEnabledTag(pkg.Comments) + ptagValue := "" + ptagRegister := false + if ptag != nil { + ptagValue = ptag.value + if ptagValue != tagValuePackage { + klog.Fatalf("Package %v: unsupported %s value: %q", i, tagEnabledName, ptagValue) + } + ptagRegister = ptag.register + klog.V(5).Infof(" tag.value: %q, tag.register: %t", ptagValue, ptagRegister) + } else { + klog.V(5).Infof(" no tag") + } + + // If the pkg-scoped tag says to generate, we can skip scanning types. + pkgNeedsGeneration := (ptagValue == tagValuePackage) + if !pkgNeedsGeneration { + // If the pkg-scoped tag did not exist, scan all types for one that + // explicitly wants generation. + for _, t := range pkg.Types { + klog.V(5).Infof(" considering type %q", t.Name.String()) + ttag := extractEnabledTypeTag(t) + if ttag != nil && ttag.value == "true" { + klog.V(5).Infof(" tag=true") + if !copyableType(t) { + klog.Fatalf("Type %v requests deepcopy generation but is not copyable", t) + } + pkgNeedsGeneration = true + break + } + } + } + + if pkgNeedsGeneration { + klog.V(3).Infof("Package %q needs generation", i) + path := pkg.Path + // if the source path is within a /vendor/ directory (for example, + // k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1), allow + // generation to output to the proper relative path (under vendor). + // Otherwise, the generator will create the file in the wrong location + // in the output directory. + // TODO: build a more fundamental concept in gengo for dealing with modifications + // to vendored packages. + if strings.HasPrefix(pkg.SourcePath, arguments.OutputBase) { + expandedPath := strings.TrimPrefix(pkg.SourcePath, arguments.OutputBase) + if strings.Contains(expandedPath, "/vendor/") { + path = expandedPath + } + } + packages = append(packages, + &generator.DefaultPackage{ + PackageName: strings.Split(filepath.Base(pkg.Path), ".")[0], + PackagePath: path, + HeaderText: header, + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + return []generator.Generator{ + NewGenDeepCopy(arguments.OutputFileBaseName, pkg.Path, boundingDirs, (ptagValue == tagValuePackage), ptagRegister), + } + }, + FilterFunc: func(c *generator.Context, t *types.Type) bool { + return t.Name.Package == pkg.Path + }, + }) + } + } + return packages +} + +// genDeepCopy produces a file with autogenerated deep-copy functions. +type genDeepCopy struct { + generator.DefaultGen + targetPackage string + boundingDirs []string + allTypes bool + registerTypes bool + imports namer.ImportTracker + typesForInit []*types.Type +} + +func NewGenDeepCopy(sanitizedName, targetPackage string, boundingDirs []string, allTypes, registerTypes bool) generator.Generator { + return &genDeepCopy{ + DefaultGen: generator.DefaultGen{ + OptionalName: sanitizedName, + }, + targetPackage: targetPackage, + boundingDirs: boundingDirs, + allTypes: allTypes, + registerTypes: registerTypes, + imports: generator.NewImportTracker(), + typesForInit: make([]*types.Type, 0), + } +} + +func (g *genDeepCopy) Namers(c *generator.Context) namer.NameSystems { + // Have the raw namer for this file track what it imports. + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.targetPackage, g.imports), + } +} + +func (g *genDeepCopy) Filter(c *generator.Context, t *types.Type) bool { + // Filter out types not being processed or not copyable within the package. + enabled := g.allTypes + if !enabled { + ttag := extractEnabledTypeTag(t) + if ttag != nil && ttag.value == "true" { + enabled = true + } + } + if !enabled { + return false + } + if !copyableType(t) { + klog.V(2).Infof("Type %v is not copyable", t) + return false + } + klog.V(4).Infof("Type %v is copyable", t) + g.typesForInit = append(g.typesForInit, t) + return true +} + +func (g *genDeepCopy) copyableAndInBounds(t *types.Type) bool { + if !copyableType(t) { + return false + } + // Only packages within the restricted range can be processed. + if !isRootedUnder(t.Name.Package, g.boundingDirs) { + return false + } + return true +} + +// deepCopyMethod returns the signature of a DeepCopy() method, nil or an error +// if the type does not match. This allows more efficient deep copy +// implementations to be defined by the type's author. The correct signature +// for a type T is: +// func (t T) DeepCopy() T +// or: +// func (t *T) DeepCopy() *T +func deepCopyMethod(t *types.Type) (*types.Signature, error) { + f, found := t.Methods["DeepCopy"] + if !found { + return nil, nil + } + if len(f.Signature.Parameters) != 0 { + return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected no parameters", t) + } + if len(f.Signature.Results) != 1 { + return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected exactly one result", t) + } + + ptrResult := f.Signature.Results[0].Kind == types.Pointer && f.Signature.Results[0].Elem.Name == t.Name + nonPtrResult := f.Signature.Results[0].Name == t.Name + + if !ptrResult && !nonPtrResult { + return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected to return %s or *%s", t, t.Name.Name, t.Name.Name) + } + + ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name + nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name + + if ptrRcvr && !ptrResult { + return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a *%s result for a *%s receiver", t, t.Name.Name, t.Name.Name) + } + if nonPtrRcvr && !nonPtrResult { + return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a %s result for a %s receiver", t, t.Name.Name, t.Name.Name) + } + + return f.Signature, nil +} + +// deepCopyMethodOrDie returns the signatrue of a DeepCopy method, nil or calls klog.Fatalf +// if the type does not match. +func deepCopyMethodOrDie(t *types.Type) *types.Signature { + ret, err := deepCopyMethod(t) + if err != nil { + klog.Fatal(err) + } + return ret +} + +// deepCopyIntoMethod returns the signature of a DeepCopyInto() method, nil or an error +// if the type is wrong. DeepCopyInto allows more efficient deep copy +// implementations to be defined by the type's author. The correct signature +// for a type T is: +// func (t T) DeepCopyInto(t *T) +// or: +// func (t *T) DeepCopyInto(t *T) +func deepCopyIntoMethod(t *types.Type) (*types.Signature, error) { + f, found := t.Methods["DeepCopyInto"] + if !found { + return nil, nil + } + if len(f.Signature.Parameters) != 1 { + return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected exactly one parameter", t) + } + if len(f.Signature.Results) != 0 { + return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected no result type", t) + } + + ptrParam := f.Signature.Parameters[0].Kind == types.Pointer && f.Signature.Parameters[0].Elem.Name == t.Name + + if !ptrParam { + return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected parameter of type *%s", t, t.Name.Name) + } + + ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name + nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name + + if !ptrRcvr && !nonPtrRcvr { + // this should never happen + return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a receiver of type %s or *%s", t, t.Name.Name, t.Name.Name) + } + + return f.Signature, nil +} + +// deepCopyIntoMethodOrDie returns the signature of a DeepCopyInto() method, nil or calls klog.Fatalf +// if the type is wrong. +func deepCopyIntoMethodOrDie(t *types.Type) *types.Signature { + ret, err := deepCopyIntoMethod(t) + if err != nil { + klog.Fatal(err) + } + return ret +} + +func isRootedUnder(pkg string, roots []string) bool { + // Add trailing / to avoid false matches, e.g. foo/bar vs foo/barn. This + // assumes that bounding dirs do not have trailing slashes. + pkg = pkg + "/" + for _, root := range roots { + if strings.HasPrefix(pkg, root+"/") { + return true + } + } + return false +} + +func copyableType(t *types.Type) bool { + // If the type opts out of copy-generation, stop. + ttag := extractEnabledTypeTag(t) + if ttag != nil && ttag.value == "false" { + return false + } + + // Filter out private types. + if namer.IsPrivateGoName(t.Name.Name) { + return false + } + + if t.Kind == types.Alias { + // if the underlying built-in is not deepcopy-able, deepcopy is opt-in through definition of custom methods. + // Note that aliases of builtins, maps, slices can have deepcopy methods. + if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil { + return true + } else { + return t.Underlying.Kind != types.Builtin || copyableType(t.Underlying) + } + } + + if t.Kind != types.Struct { + return false + } + + return true +} + +func underlyingType(t *types.Type) *types.Type { + for t.Kind == types.Alias { + t = t.Underlying + } + return t +} + +func (g *genDeepCopy) isOtherPackage(pkg string) bool { + if pkg == g.targetPackage { + return false + } + if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") { + return false + } + return true +} + +func (g *genDeepCopy) Imports(c *generator.Context) (imports []string) { + importLines := []string{} + for _, singleImport := range g.imports.ImportLines() { + if g.isOtherPackage(singleImport) { + importLines = append(importLines, singleImport) + } + } + return importLines +} + +func argsFromType(ts ...*types.Type) generator.Args { + a := generator.Args{ + "type": ts[0], + } + for i, t := range ts { + a[fmt.Sprintf("type%d", i+1)] = t + } + return a +} + +func (g *genDeepCopy) Init(c *generator.Context, w io.Writer) error { + return nil +} + +func (g *genDeepCopy) needsGeneration(t *types.Type) bool { + tag := extractEnabledTypeTag(t) + tv := "" + if tag != nil { + tv = tag.value + if tv != "true" && tv != "false" { + klog.Fatalf("Type %v: unsupported %s value: %q", t, tagEnabledName, tag.value) + } + } + if g.allTypes && tv == "false" { + // The whole package is being generated, but this type has opted out. + klog.V(5).Infof("Not generating for type %v because type opted out", t) + return false + } + if !g.allTypes && tv != "true" { + // The whole package is NOT being generated, and this type has NOT opted in. + klog.V(5).Infof("Not generating for type %v because type did not opt in", t) + return false + } + return true +} + +func extractInterfacesTag(t *types.Type) []string { + var result []string + comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...) + values := types.ExtractCommentTags("+", comments)[interfacesTagName] + for _, v := range values { + if len(v) == 0 { + continue + } + intfs := strings.Split(v, ",") + for _, intf := range intfs { + if intf == "" { + continue + } + result = append(result, intf) + } + } + return result +} + +func extractNonPointerInterfaces(t *types.Type) (bool, error) { + comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...) + values := types.ExtractCommentTags("+", comments)[interfacesNonPointerTagName] + if len(values) == 0 { + return false, nil + } + result := values[0] == "true" + for _, v := range values { + if v == "true" != result { + return false, fmt.Errorf("contradicting %v value %q found to previous value %v", interfacesNonPointerTagName, v, result) + } + } + return result, nil +} + +func (g *genDeepCopy) deepCopyableInterfacesInner(c *generator.Context, t *types.Type) ([]*types.Type, error) { + if t.Kind != types.Struct { + return nil, nil + } + + intfs := extractInterfacesTag(t) + + var ts []*types.Type + for _, intf := range intfs { + t := types.ParseFullyQualifiedName(intf) + c.AddDir(t.Package) + intfT := c.Universe.Type(t) + if intfT == nil { + return nil, fmt.Errorf("unknown type %q in %s tag of type %s", intf, interfacesTagName, intfT) + } + if intfT.Kind != types.Interface { + return nil, fmt.Errorf("type %q in %s tag of type %s is not an interface, but: %q", intf, interfacesTagName, t, intfT.Kind) + } + g.imports.AddType(intfT) + ts = append(ts, intfT) + } + + return ts, nil +} + +// deepCopyableInterfaces returns the interface types to implement and whether they apply to a non-pointer receiver. +func (g *genDeepCopy) deepCopyableInterfaces(c *generator.Context, t *types.Type) ([]*types.Type, bool, error) { + ts, err := g.deepCopyableInterfacesInner(c, t) + if err != nil { + return nil, false, err + } + + set := map[string]*types.Type{} + for _, t := range ts { + set[t.String()] = t + } + + result := []*types.Type{} + for _, t := range set { + result = append(result, t) + } + + TypeSlice(result).Sort() // we need a stable sorting because it determines the order in generation + + nonPointerReceiver, err := extractNonPointerInterfaces(t) + if err != nil { + return nil, false, err + } + + return result, nonPointerReceiver, nil +} + +type TypeSlice []*types.Type + +func (s TypeSlice) Len() int { return len(s) } +func (s TypeSlice) Less(i, j int) bool { return s[i].String() < s[j].String() } +func (s TypeSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s TypeSlice) Sort() { sort.Sort(s) } + +func (g *genDeepCopy) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + if !g.needsGeneration(t) { + return nil + } + klog.V(5).Infof("Generating deepcopy function for type %v", t) + + sw := generator.NewSnippetWriter(w, c, "$", "$") + args := argsFromType(t) + + if deepCopyIntoMethodOrDie(t) == nil { + sw.Do("// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\n", args) + if isReference(t) { + sw.Do("func (in $.type|raw$) DeepCopyInto(out *$.type|raw$) {\n", args) + sw.Do("{in:=&in\n", nil) + } else { + sw.Do("func (in *$.type|raw$) DeepCopyInto(out *$.type|raw$) {\n", args) + } + if deepCopyMethodOrDie(t) != nil { + if t.Methods["DeepCopy"].Signature.Receiver.Kind == types.Pointer { + sw.Do("clone := in.DeepCopy()\n", nil) + sw.Do("*out = *clone\n", nil) + } else { + sw.Do("*out = in.DeepCopy()\n", nil) + } + sw.Do("return\n", nil) + } else { + g.generateFor(t, sw) + sw.Do("return\n", nil) + } + if isReference(t) { + sw.Do("}\n", nil) + } + sw.Do("}\n\n", nil) + } + + if deepCopyMethodOrDie(t) == nil { + sw.Do("// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new $.type|raw$.\n", args) + if isReference(t) { + sw.Do("func (in $.type|raw$) DeepCopy() $.type|raw$ {\n", args) + } else { + sw.Do("func (in *$.type|raw$) DeepCopy() *$.type|raw$ {\n", args) + } + sw.Do("if in == nil { return nil }\n", nil) + sw.Do("out := new($.type|raw$)\n", args) + sw.Do("in.DeepCopyInto(out)\n", nil) + if isReference(t) { + sw.Do("return *out\n", nil) + } else { + sw.Do("return out\n", nil) + } + sw.Do("}\n\n", nil) + } + + intfs, nonPointerReceiver, err := g.deepCopyableInterfaces(c, t) + if err != nil { + return err + } + for _, intf := range intfs { + sw.Do(fmt.Sprintf("// DeepCopy%s is an autogenerated deepcopy function, copying the receiver, creating a new $.type2|raw$.\n", intf.Name.Name), argsFromType(t, intf)) + if nonPointerReceiver { + sw.Do(fmt.Sprintf("func (in $.type|raw$) DeepCopy%s() $.type2|raw$ {\n", intf.Name.Name), argsFromType(t, intf)) + sw.Do("return *in.DeepCopy()", nil) + sw.Do("}\n\n", nil) + } else { + sw.Do(fmt.Sprintf("func (in *$.type|raw$) DeepCopy%s() $.type2|raw$ {\n", intf.Name.Name), argsFromType(t, intf)) + sw.Do("if c := in.DeepCopy(); c != nil {\n", nil) + sw.Do("return c\n", nil) + sw.Do("}\n", nil) + sw.Do("return nil\n", nil) + sw.Do("}\n\n", nil) + } + } + + return sw.Error() +} + +// isReference return true for pointer, maps, slices and aliases of those. +func isReference(t *types.Type) bool { + if t.Kind == types.Pointer || t.Kind == types.Map || t.Kind == types.Slice { + return true + } + return t.Kind == types.Alias && isReference(underlyingType(t)) +} + +// we use the system of shadowing 'in' and 'out' so that the same code is valid +// at any nesting level. This makes the autogenerator easy to understand, and +// the compiler shouldn't care. +func (g *genDeepCopy) generateFor(t *types.Type, sw *generator.SnippetWriter) { + // derive inner types if t is an alias. We call the do* methods below with the alias type. + // basic rule: generate according to inner type, but construct objects with the alias type. + ut := underlyingType(t) + + var f func(*types.Type, *generator.SnippetWriter) + switch ut.Kind { + case types.Builtin: + f = g.doBuiltin + case types.Map: + f = g.doMap + case types.Slice: + f = g.doSlice + case types.Struct: + f = g.doStruct + case types.Pointer: + f = g.doPointer + case types.Interface: + // interfaces are handled in-line in the other cases + klog.Fatalf("Hit an interface type %v. This should never happen.", t) + case types.Alias: + // can never happen because we branch on the underlying type which is never an alias + klog.Fatalf("Hit an alias type %v. This should never happen.", t) + default: + klog.Fatalf("Hit an unsupported type %v.", t) + } + f(t, sw) +} + +// doBuiltin generates code for a builtin or an alias to a builtin. The generated code is +// is the same for both cases, i.e. it's the code for the underlying type. +func (g *genDeepCopy) doBuiltin(t *types.Type, sw *generator.SnippetWriter) { + if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil { + sw.Do("*out = in.DeepCopy()\n", nil) + return + } + + sw.Do("*out = *in\n", nil) +} + +// doMap generates code for a map or an alias to a map. The generated code is +// is the same for both cases, i.e. it's the code for the underlying type. +func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) { + ut := underlyingType(t) + uet := underlyingType(ut.Elem) + + if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil { + sw.Do("*out = in.DeepCopy()\n", nil) + return + } + + if !ut.Key.IsAssignable() { + klog.Fatalf("Hit an unsupported type %v for: %v", uet, t) + } + + sw.Do("*out = make($.|raw$, len(*in))\n", t) + sw.Do("for key, val := range *in {\n", nil) + dc, dci := deepCopyMethodOrDie(ut.Elem), deepCopyIntoMethodOrDie(ut.Elem) + switch { + case dc != nil || dci != nil: + // Note: a DeepCopy exists because it is added if DeepCopyInto is manually defined + leftPointer := ut.Elem.Kind == types.Pointer + rightPointer := !isReference(ut.Elem) + if dc != nil { + rightPointer = dc.Results[0].Kind == types.Pointer + } + if leftPointer == rightPointer { + sw.Do("(*out)[key] = val.DeepCopy()\n", nil) + } else if leftPointer { + sw.Do("x := val.DeepCopy()\n", nil) + sw.Do("(*out)[key] = &x\n", nil) + } else { + sw.Do("(*out)[key] = *val.DeepCopy()\n", nil) + } + case ut.Elem.IsAnonymousStruct(): // not uet here because it needs type cast + sw.Do("(*out)[key] = val\n", nil) + case uet.IsAssignable(): + sw.Do("(*out)[key] = val\n", nil) + case uet.Kind == types.Interface: + // Note: do not generate code that won't compile as `DeepCopyinterface{}()` is not a valid function + if uet.Name.Name == "interface{}" { + klog.Fatalf("DeepCopy of %q is unsupported. Instead, use named interfaces with DeepCopy as one of the methods.", uet.Name.Name) + } + sw.Do("if val == nil {(*out)[key]=nil} else {\n", nil) + // Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it + // as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang + // parser does not give us the underlying interface name. So we cannot do any better. + sw.Do(fmt.Sprintf("(*out)[key] = val.DeepCopy%s()\n", uet.Name.Name), nil) + sw.Do("}\n", nil) + case uet.Kind == types.Slice || uet.Kind == types.Map || uet.Kind == types.Pointer: + sw.Do("var outVal $.|raw$\n", uet) + sw.Do("if val == nil { (*out)[key] = nil } else {\n", nil) + sw.Do("in, out := &val, &outVal\n", uet) + g.generateFor(ut.Elem, sw) + sw.Do("}\n", nil) + sw.Do("(*out)[key] = outVal\n", nil) + case uet.Kind == types.Struct: + sw.Do("(*out)[key] = *val.DeepCopy()\n", uet) + default: + klog.Fatalf("Hit an unsupported type %v for %v", uet, t) + } + sw.Do("}\n", nil) +} + +// doSlice generates code for a slice or an alias to a slice. The generated code is +// is the same for both cases, i.e. it's the code for the underlying type. +func (g *genDeepCopy) doSlice(t *types.Type, sw *generator.SnippetWriter) { + ut := underlyingType(t) + uet := underlyingType(ut.Elem) + + if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil { + sw.Do("*out = in.DeepCopy()\n", nil) + return + } + + sw.Do("*out = make($.|raw$, len(*in))\n", t) + if deepCopyMethodOrDie(ut.Elem) != nil || deepCopyIntoMethodOrDie(ut.Elem) != nil { + sw.Do("for i := range *in {\n", nil) + // Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined + sw.Do("(*in)[i].DeepCopyInto(&(*out)[i])\n", nil) + sw.Do("}\n", nil) + } else if uet.Kind == types.Builtin || uet.IsAssignable() { + sw.Do("copy(*out, *in)\n", nil) + } else { + sw.Do("for i := range *in {\n", nil) + if uet.Kind == types.Slice || uet.Kind == types.Map || uet.Kind == types.Pointer || deepCopyMethodOrDie(ut.Elem) != nil || deepCopyIntoMethodOrDie(ut.Elem) != nil { + sw.Do("if (*in)[i] != nil {\n", nil) + sw.Do("in, out := &(*in)[i], &(*out)[i]\n", nil) + g.generateFor(ut.Elem, sw) + sw.Do("}\n", nil) + } else if uet.Kind == types.Interface { + // Note: do not generate code that won't compile as `DeepCopyinterface{}()` is not a valid function + if uet.Name.Name == "interface{}" { + klog.Fatalf("DeepCopy of %q is unsupported. Instead, use named interfaces with DeepCopy as one of the methods.", uet.Name.Name) + } + sw.Do("if (*in)[i] != nil {\n", nil) + // Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it + // as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang + // parser does not give us the underlying interface name. So we cannot do any better. + sw.Do(fmt.Sprintf("(*out)[i] = (*in)[i].DeepCopy%s()\n", uet.Name.Name), nil) + sw.Do("}\n", nil) + } else if uet.Kind == types.Struct { + sw.Do("(*in)[i].DeepCopyInto(&(*out)[i])\n", nil) + } else { + klog.Fatalf("Hit an unsupported type %v for %v", uet, t) + } + sw.Do("}\n", nil) + } +} + +// doStruct generates code for a struct or an alias to a struct. The generated code is +// is the same for both cases, i.e. it's the code for the underlying type. +func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) { + ut := underlyingType(t) + + if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil { + sw.Do("*out = in.DeepCopy()\n", nil) + return + } + + // Simple copy covers a lot of cases. + sw.Do("*out = *in\n", nil) + + // Now fix-up fields as needed. + for _, m := range ut.Members { + ft := m.Type + uft := underlyingType(ft) + + args := generator.Args{ + "type": ft, + "kind": ft.Kind, + "name": m.Name, + } + dc, dci := deepCopyMethodOrDie(ft), deepCopyIntoMethodOrDie(ft) + switch { + case dc != nil || dci != nil: + // Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined + leftPointer := ft.Kind == types.Pointer + rightPointer := !isReference(ft) + if dc != nil { + rightPointer = dc.Results[0].Kind == types.Pointer + } + if leftPointer == rightPointer { + sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args) + } else if leftPointer { + sw.Do("x := in.$.name$.DeepCopy()\n", args) + sw.Do("out.$.name$ = = &x\n", args) + } else { + sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args) + } + case uft.Kind == types.Builtin: + // the initial *out = *in was enough + case uft.Kind == types.Map, uft.Kind == types.Slice, uft.Kind == types.Pointer: + // Fixup non-nil reference-semantic types. + sw.Do("if in.$.name$ != nil {\n", args) + sw.Do("in, out := &in.$.name$, &out.$.name$\n", args) + g.generateFor(ft, sw) + sw.Do("}\n", nil) + case uft.Kind == types.Struct: + if ft.IsAssignable() { + sw.Do("out.$.name$ = in.$.name$\n", args) + } else { + sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args) + } + case uft.Kind == types.Interface: + // Note: do not generate code that won't compile as `DeepCopyinterface{}()` is not a valid function + if uft.Name.Name == "interface{}" { + klog.Fatalf("DeepCopy of %q is unsupported. Instead, use named interfaces with DeepCopy as one of the methods.", uft.Name.Name) + } + sw.Do("if in.$.name$ != nil {\n", args) + // Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it + // as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang + // parser does not give us the underlying interface name. So we cannot do any better. + sw.Do(fmt.Sprintf("out.$.name$ = in.$.name$.DeepCopy%s()\n", uft.Name.Name), args) + sw.Do("}\n", nil) + default: + klog.Fatalf("Hit an unsupported type %v for %v, from %v", uft, ft, t) + } + } +} + +// doPointer generates code for a pointer or an alias to a pointer. The generated code is +// is the same for both cases, i.e. it's the code for the underlying type. +func (g *genDeepCopy) doPointer(t *types.Type, sw *generator.SnippetWriter) { + ut := underlyingType(t) + uet := underlyingType(ut.Elem) + + dc, dci := deepCopyMethodOrDie(ut.Elem), deepCopyIntoMethodOrDie(ut.Elem) + switch { + case dc != nil || dci != nil: + rightPointer := !isReference(ut.Elem) + if dc != nil { + rightPointer = dc.Results[0].Kind == types.Pointer + } + if rightPointer { + sw.Do("*out = (*in).DeepCopy()\n", nil) + } else { + sw.Do("x := (*in).DeepCopy()\n", nil) + sw.Do("*out = &x\n", nil) + } + case uet.IsAssignable(): + sw.Do("*out = new($.Elem|raw$)\n", ut) + sw.Do("**out = **in", nil) + case uet.Kind == types.Map, uet.Kind == types.Slice, uet.Kind == types.Pointer: + sw.Do("*out = new($.Elem|raw$)\n", ut) + sw.Do("if **in != nil {\n", nil) + sw.Do("in, out := *in, *out\n", nil) + g.generateFor(uet, sw) + sw.Do("}\n", nil) + case uet.Kind == types.Struct: + sw.Do("*out = new($.Elem|raw$)\n", ut) + sw.Do("(*in).DeepCopyInto(*out)\n", nil) + default: + klog.Fatalf("Hit an unsupported type %v for %v", uet, t) + } +} diff --git a/vendor/k8s.io/gengo/examples/defaulter-gen/generators/defaulter.go b/vendor/k8s.io/gengo/examples/defaulter-gen/generators/defaulter.go new file mode 100644 index 0000000000..7e3bc6b4b1 --- /dev/null +++ b/vendor/k8s.io/gengo/examples/defaulter-gen/generators/defaulter.go @@ -0,0 +1,828 @@ +/* +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 generators + +import ( + "bytes" + "fmt" + "io" + "path/filepath" + "reflect" + "strings" + + "k8s.io/gengo/args" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/klog" +) + +// CustomArgs is used tby the go2idl framework to pass args specific to this +// generator. +type CustomArgs struct { + ExtraPeerDirs []string // Always consider these as last-ditch possibilities for conversions. +} + +// These are the comment tags that carry parameters for defaulter generation. +const tagName = "k8s:defaulter-gen" +const intputTagName = "k8s:defaulter-gen-input" + +func extractTag(comments []string) []string { + return types.ExtractCommentTags("+", comments)[tagName] +} + +func extractInputTag(comments []string) []string { + return types.ExtractCommentTags("+", comments)[intputTagName] +} + +func checkTag(comments []string, require ...string) bool { + values := types.ExtractCommentTags("+", comments)[tagName] + if len(require) == 0 { + return len(values) == 1 && values[0] == "" + } + return reflect.DeepEqual(values, require) +} + +func defaultFnNamer() *namer.NameStrategy { + return &namer.NameStrategy{ + Prefix: "SetDefaults_", + Join: func(pre string, in []string, post string) string { + return pre + strings.Join(in, "_") + post + }, + } +} + +func objectDefaultFnNamer() *namer.NameStrategy { + return &namer.NameStrategy{ + Prefix: "SetObjectDefaults_", + Join: func(pre string, in []string, post string) string { + return pre + strings.Join(in, "_") + post + }, + } +} + +// NameSystems returns the name system used by the generators in this package. +func NameSystems() namer.NameSystems { + return namer.NameSystems{ + "public": namer.NewPublicNamer(1), + "raw": namer.NewRawNamer("", nil), + "defaultfn": defaultFnNamer(), + "objectdefaultfn": objectDefaultFnNamer(), + } +} + +// DefaultNameSystem returns the default name system for ordering the types to be +// processed by the generators in this package. +func DefaultNameSystem() string { + return "public" +} + +// defaults holds the declared defaulting functions for a given type (all defaulting functions +// are expected to be func(1)) +type defaults struct { + // object is the defaulter function for a top level type (typically one with TypeMeta) that + // invokes all child defaulters. May be nil if the object defaulter has not yet been generated. + object *types.Type + // base is a defaulter function defined for a type SetDefaults_Pod which does not invoke all + // child defaults - the base defaulter alone is insufficient to default a type + base *types.Type + // additional is zero or more defaulter functions of the form SetDefaults_Pod_XXXX that can be + // included in the Object defaulter. + additional []*types.Type +} + +// All of the types in conversions map are of type "DeclarationOf" with +// the underlying type being "Func". +type defaulterFuncMap map[*types.Type]defaults + +// Returns all manually-defined defaulting functions in the package. +func getManualDefaultingFunctions(context *generator.Context, pkg *types.Package, manualMap defaulterFuncMap) { + buffer := &bytes.Buffer{} + sw := generator.NewSnippetWriter(buffer, context, "$", "$") + + for _, f := range pkg.Functions { + if f.Underlying == nil || f.Underlying.Kind != types.Func { + klog.Errorf("Malformed function: %#v", f) + continue + } + if f.Underlying.Signature == nil { + klog.Errorf("Function without signature: %#v", f) + continue + } + signature := f.Underlying.Signature + // Check whether the function is defaulting function. + // Note that all of them have signature: + // object: func SetObjectDefaults_inType(*inType) + // base: func SetDefaults_inType(*inType) + // additional: func SetDefaults_inType_Qualifier(*inType) + if signature.Receiver != nil { + continue + } + if len(signature.Parameters) != 1 { + continue + } + if len(signature.Results) != 0 { + continue + } + inType := signature.Parameters[0] + if inType.Kind != types.Pointer { + continue + } + // Check if this is the primary defaulter. + args := defaultingArgsFromType(inType.Elem) + sw.Do("$.inType|defaultfn$", args) + switch { + case f.Name.Name == buffer.String(): + key := inType.Elem + // We might scan the same package twice, and that's OK. + v, ok := manualMap[key] + if ok && v.base != nil && v.base.Name.Package != pkg.Path { + panic(fmt.Sprintf("duplicate static defaulter defined: %#v", key)) + } + v.base = f + manualMap[key] = v + klog.V(6).Infof("found base defaulter function for %s from %s", key.Name, f.Name) + // Is one of the additional defaulters - a top level defaulter on a type that is + // also invoked. + case strings.HasPrefix(f.Name.Name, buffer.String()+"_"): + key := inType.Elem + v, ok := manualMap[key] + if ok { + exists := false + for _, existing := range v.additional { + if existing.Name == f.Name { + exists = true + break + } + } + if exists { + continue + } + } + v.additional = append(v.additional, f) + manualMap[key] = v + klog.V(6).Infof("found additional defaulter function for %s from %s", key.Name, f.Name) + } + buffer.Reset() + sw.Do("$.inType|objectdefaultfn$", args) + if f.Name.Name == buffer.String() { + key := inType.Elem + // We might scan the same package twice, and that's OK. + v, ok := manualMap[key] + if ok && v.base != nil && v.base.Name.Package != pkg.Path { + panic(fmt.Sprintf("duplicate static defaulter defined: %#v", key)) + } + v.object = f + manualMap[key] = v + klog.V(6).Infof("found object defaulter function for %s from %s", key.Name, f.Name) + } + buffer.Reset() + } +} + +func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { + boilerplate, err := arguments.LoadGoBoilerplate() + if err != nil { + klog.Fatalf("Failed loading boilerplate: %v", err) + } + + packages := generator.Packages{} + header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...) + + // Accumulate pre-existing default functions. + // TODO: This is too ad-hoc. We need a better way. + existingDefaulters := defaulterFuncMap{} + + buffer := &bytes.Buffer{} + sw := generator.NewSnippetWriter(buffer, context, "$", "$") + + // We are generating defaults only for packages that are explicitly + // passed as InputDir. + for _, i := range context.Inputs { + klog.V(5).Infof("considering pkg %q", i) + pkg := context.Universe[i] + if pkg == nil { + // If the input had no Go files, for example. + continue + } + // typesPkg is where the types that needs defaulter are defined. + // Sometimes it is different from pkg. For example, kubernetes core/v1 + // types are defined in vendor/k8s.io/api/core/v1, while pkg is at + // pkg/api/v1. + typesPkg := pkg + + // Add defaulting functions. + getManualDefaultingFunctions(context, pkg, existingDefaulters) + + var peerPkgs []string + if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok { + for _, pkg := range customArgs.ExtraPeerDirs { + if i := strings.Index(pkg, "/vendor/"); i != -1 { + pkg = pkg[i+len("/vendor/"):] + } + peerPkgs = append(peerPkgs, pkg) + } + } + // Make sure our peer-packages are added and fully parsed. + for _, pp := range peerPkgs { + context.AddDir(pp) + getManualDefaultingFunctions(context, context.Universe[pp], existingDefaulters) + } + + typesWith := extractTag(pkg.Comments) + shouldCreateObjectDefaulterFn := func(t *types.Type) bool { + if defaults, ok := existingDefaulters[t]; ok && defaults.object != nil { + // A default generator is defined + klog.V(5).Infof(" an object defaulter already exists as %s", defaults.base.Name) + return false + } + // opt-out + if checkTag(t.SecondClosestCommentLines, "false") { + return false + } + // opt-in + if checkTag(t.SecondClosestCommentLines, "true") { + return true + } + // For every k8s:defaulter-gen tag at the package level, interpret the value as a + // field name (like TypeMeta, ListMeta, ObjectMeta) and trigger defaulter generation + // for any type with any of the matching field names. Provides a more useful package + // level defaulting than global (because we only need defaulters on a subset of objects - + // usually those with TypeMeta). + if t.Kind == types.Struct && len(typesWith) > 0 { + for _, field := range t.Members { + for _, s := range typesWith { + if field.Name == s { + return true + } + } + } + } + return false + } + + // if the types are not in the same package where the defaulter functions to be generated + inputTags := extractInputTag(pkg.Comments) + if len(inputTags) > 1 { + panic(fmt.Sprintf("there could only be one input tag, got %#v", inputTags)) + } + if len(inputTags) == 1 { + var err error + typesPkg, err = context.AddDirectory(filepath.Join(pkg.Path, inputTags[0])) + if err != nil { + klog.Fatalf("cannot import package %s", inputTags[0]) + } + // update context.Order to the latest context.Universe + orderer := namer.Orderer{Namer: namer.NewPublicNamer(1)} + context.Order = orderer.OrderUniverse(context.Universe) + } + + newDefaulters := defaulterFuncMap{} + for _, t := range typesPkg.Types { + if !shouldCreateObjectDefaulterFn(t) { + continue + } + if namer.IsPrivateGoName(t.Name.Name) { + // We won't be able to convert to a private type. + klog.V(5).Infof(" found a type %v, but it is a private name", t) + continue + } + + // create a synthetic type we can use during generation + newDefaulters[t] = defaults{} + } + + // only generate defaulters for objects that actually have defined defaulters + // prevents empty defaulters from being registered + for { + promoted := 0 + for t, d := range newDefaulters { + if d.object != nil { + continue + } + if newCallTreeForType(existingDefaulters, newDefaulters).build(t, true) != nil { + args := defaultingArgsFromType(t) + sw.Do("$.inType|objectdefaultfn$", args) + newDefaulters[t] = defaults{ + object: &types.Type{ + Name: types.Name{ + Package: pkg.Path, + Name: buffer.String(), + }, + Kind: types.Func, + }, + } + buffer.Reset() + promoted++ + } + } + if promoted != 0 { + continue + } + + // prune any types that were not used + for t, d := range newDefaulters { + if d.object == nil { + klog.V(6).Infof("did not generate defaulter for %s because no child defaulters were registered", t.Name) + delete(newDefaulters, t) + } + } + break + } + + if len(newDefaulters) == 0 { + klog.V(5).Infof("no defaulters in package %s", pkg.Name) + } + + path := pkg.Path + // if the source path is within a /vendor/ directory (for example, + // k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1), allow + // generation to output to the proper relative path (under vendor). + // Otherwise, the generator will create the file in the wrong location + // in the output directory. + // TODO: build a more fundamental concept in gengo for dealing with modifications + // to vendored packages. + if strings.HasPrefix(pkg.SourcePath, arguments.OutputBase) { + expandedPath := strings.TrimPrefix(pkg.SourcePath, arguments.OutputBase) + if strings.Contains(expandedPath, "/vendor/") { + path = expandedPath + } + } + + packages = append(packages, + &generator.DefaultPackage{ + PackageName: filepath.Base(pkg.Path), + PackagePath: path, + HeaderText: header, + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + return []generator.Generator{ + NewGenDefaulter(arguments.OutputFileBaseName, typesPkg.Path, pkg.Path, existingDefaulters, newDefaulters, peerPkgs), + } + }, + FilterFunc: func(c *generator.Context, t *types.Type) bool { + return t.Name.Package == typesPkg.Path + }, + }) + } + return packages +} + +// callTreeForType contains fields necessary to build a tree for types. +type callTreeForType struct { + existingDefaulters defaulterFuncMap + newDefaulters defaulterFuncMap + currentlyBuildingTypes map[*types.Type]bool +} + +func newCallTreeForType(existingDefaulters, newDefaulters defaulterFuncMap) *callTreeForType { + return &callTreeForType{ + existingDefaulters: existingDefaulters, + newDefaulters: newDefaulters, + currentlyBuildingTypes: make(map[*types.Type]bool), + } +} + +// build creates a tree of paths to fields (based on how they would be accessed in Go - pointer, elem, +// slice, or key) and the functions that should be invoked on each field. An in-order traversal of the resulting tree +// can be used to generate a Go function that invokes each nested function on the appropriate type. The return +// value may be nil if there are no functions to call on type or the type is a primitive (Defaulters can only be +// invoked on structs today). When root is true this function will not use a newDefaulter. existingDefaulters should +// contain all defaulting functions by type defined in code - newDefaulters should contain all object defaulters +// that could be or will be generated. If newDefaulters has an entry for a type, but the 'object' field is nil, +// this function skips adding that defaulter - this allows us to avoid generating object defaulter functions for +// list types that call empty defaulters. +func (c *callTreeForType) build(t *types.Type, root bool) *callNode { + parent := &callNode{} + + if root { + // the root node is always a pointer + parent.elem = true + } + + defaults, _ := c.existingDefaulters[t] + newDefaults, generated := c.newDefaulters[t] + switch { + case !root && generated && newDefaults.object != nil: + parent.call = append(parent.call, newDefaults.object) + // if we will be generating the defaulter, it by definition is a covering + // defaulter, so we halt recursion + klog.V(6).Infof("the defaulter %s will be generated as an object defaulter", t.Name) + return parent + + case defaults.object != nil: + // object defaulters are always covering + parent.call = append(parent.call, defaults.object) + return parent + + case defaults.base != nil: + parent.call = append(parent.call, defaults.base) + // if the base function indicates it "covers" (it already includes defaulters) + // we can halt recursion + if checkTag(defaults.base.CommentLines, "covers") { + klog.V(6).Infof("the defaulter %s indicates it covers all sub generators", t.Name) + return parent + } + } + + // base has been added already, now add any additional defaulters defined for this object + parent.call = append(parent.call, defaults.additional...) + + // if the type already exists, don't build the tree for it and don't generate anything. + // This is used to avoid recursion for nested recursive types. + if c.currentlyBuildingTypes[t] { + return nil + } + // if type doesn't exist, mark it as existing + c.currentlyBuildingTypes[t] = true + + defer func() { + // The type will now acts as a parent, not a nested recursive type. + // We can now build the tree for it safely. + c.currentlyBuildingTypes[t] = false + }() + + switch t.Kind { + case types.Pointer: + if child := c.build(t.Elem, false); child != nil { + child.elem = true + parent.children = append(parent.children, *child) + } + case types.Slice, types.Array: + if child := c.build(t.Elem, false); child != nil { + child.index = true + if t.Elem.Kind == types.Pointer { + child.elem = true + } + parent.children = append(parent.children, *child) + } + case types.Map: + if child := c.build(t.Elem, false); child != nil { + child.key = true + parent.children = append(parent.children, *child) + } + case types.Struct: + for _, field := range t.Members { + name := field.Name + if len(name) == 0 { + if field.Type.Kind == types.Pointer { + name = field.Type.Elem.Name.Name + } else { + name = field.Type.Name.Name + } + } + if child := c.build(field.Type, false); child != nil { + child.field = name + parent.children = append(parent.children, *child) + } + } + case types.Alias: + if child := c.build(t.Underlying, false); child != nil { + parent.children = append(parent.children, *child) + } + } + if len(parent.children) == 0 && len(parent.call) == 0 { + //klog.V(6).Infof("decided type %s needs no generation", t.Name) + return nil + } + return parent +} + +const ( + runtimePackagePath = "k8s.io/apimachinery/pkg/runtime" + conversionPackagePath = "k8s.io/apimachinery/pkg/conversion" +) + +// genDefaulter produces a file with a autogenerated conversions. +type genDefaulter struct { + generator.DefaultGen + typesPackage string + outputPackage string + peerPackages []string + newDefaulters defaulterFuncMap + existingDefaulters defaulterFuncMap + imports namer.ImportTracker + typesForInit []*types.Type +} + +func NewGenDefaulter(sanitizedName, typesPackage, outputPackage string, existingDefaulters, newDefaulters defaulterFuncMap, peerPkgs []string) generator.Generator { + return &genDefaulter{ + DefaultGen: generator.DefaultGen{ + OptionalName: sanitizedName, + }, + typesPackage: typesPackage, + outputPackage: outputPackage, + peerPackages: peerPkgs, + newDefaulters: newDefaulters, + existingDefaulters: existingDefaulters, + imports: generator.NewImportTracker(), + typesForInit: make([]*types.Type, 0), + } +} + +func (g *genDefaulter) Namers(c *generator.Context) namer.NameSystems { + // Have the raw namer for this file track what it imports. + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *genDefaulter) isOtherPackage(pkg string) bool { + if pkg == g.outputPackage { + return false + } + if strings.HasSuffix(pkg, `"`+g.outputPackage+`"`) { + return false + } + return true +} + +func (g *genDefaulter) Filter(c *generator.Context, t *types.Type) bool { + defaults, ok := g.newDefaulters[t] + if !ok || defaults.object == nil { + return false + } + g.typesForInit = append(g.typesForInit, t) + return true +} + +func (g *genDefaulter) Imports(c *generator.Context) (imports []string) { + var importLines []string + for _, singleImport := range g.imports.ImportLines() { + if g.isOtherPackage(singleImport) { + importLines = append(importLines, singleImport) + } + } + return importLines +} + +func (g *genDefaulter) Init(c *generator.Context, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + + scheme := c.Universe.Type(types.Name{Package: runtimePackagePath, Name: "Scheme"}) + schemePtr := &types.Type{ + Kind: types.Pointer, + Elem: scheme, + } + sw.Do("// RegisterDefaults adds defaulters functions to the given scheme.\n", nil) + sw.Do("// Public to allow building arbitrary schemes.\n", nil) + sw.Do("// All generated defaulters are covering - they call all nested defaulters.\n", nil) + sw.Do("func RegisterDefaults(scheme $.|raw$) error {\n", schemePtr) + for _, t := range g.typesForInit { + args := defaultingArgsFromType(t) + sw.Do("scheme.AddTypeDefaultingFunc(&$.inType|raw${}, func(obj interface{}) { $.inType|objectdefaultfn$(obj.(*$.inType|raw$)) })\n", args) + } + sw.Do("return nil\n", nil) + sw.Do("}\n\n", nil) + return sw.Error() +} + +func (g *genDefaulter) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + if _, ok := g.newDefaulters[t]; !ok { + return nil + } + + klog.V(5).Infof("generating for type %v", t) + + callTree := newCallTreeForType(g.existingDefaulters, g.newDefaulters).build(t, true) + if callTree == nil { + klog.V(5).Infof(" no defaulters defined") + return nil + } + i := 0 + callTree.VisitInOrder(func(ancestors []*callNode, current *callNode) { + if len(current.call) == 0 { + return + } + path := callPath(append(ancestors, current)) + klog.V(5).Infof(" %d: %s", i, path) + i++ + }) + + sw := generator.NewSnippetWriter(w, c, "$", "$") + g.generateDefaulter(t, callTree, sw) + return sw.Error() +} + +func defaultingArgsFromType(inType *types.Type) generator.Args { + return generator.Args{ + "inType": inType, + } +} + +func (g *genDefaulter) generateDefaulter(inType *types.Type, callTree *callNode, sw *generator.SnippetWriter) { + sw.Do("func $.inType|objectdefaultfn$(in *$.inType|raw$) {\n", defaultingArgsFromType(inType)) + callTree.WriteMethod("in", 0, nil, sw) + sw.Do("}\n\n", nil) +} + +// callNode represents an entry in a tree of Go type accessors - the path from the root to a leaf represents +// how in Go code an access would be performed. For example, if a defaulting function exists on a container +// lifecycle hook, to invoke that defaulter correctly would require this Go code: +// +// for i := range pod.Spec.Containers { +// o := &pod.Spec.Containers[i] +// if o.LifecycleHook != nil { +// SetDefaults_LifecycleHook(o.LifecycleHook) +// } +// } +// +// That would be represented by a call tree like: +// +// callNode +// field: "Spec" +// children: +// - field: "Containers" +// children: +// - index: true +// children: +// - field: "LifecycleHook" +// elem: true +// call: +// - SetDefaults_LifecycleHook +// +// which we can traverse to build that Go struct (you must call the field Spec, then Containers, then range over +// that field, then check whether the LifecycleHook field is nil, before calling SetDefaults_LifecycleHook on +// the pointer to that field). +type callNode struct { + // field is the name of the Go member to access + field string + // key is true if this is a map and we must range over the key and values + key bool + // index is true if this is a slice and we must range over the slice values + index bool + // elem is true if the previous elements refer to a pointer (typically just field) + elem bool + + // call is all of the functions that must be invoked on this particular node, in order + call []*types.Type + // children is the child call nodes that must also be traversed + children []callNode +} + +// CallNodeVisitorFunc is a function for visiting a call tree. ancestors is the list of all parents +// of this node to the root of the tree - will be empty at the root. +type CallNodeVisitorFunc func(ancestors []*callNode, node *callNode) + +func (n *callNode) VisitInOrder(fn CallNodeVisitorFunc) { + n.visitInOrder(nil, fn) +} + +func (n *callNode) visitInOrder(ancestors []*callNode, fn CallNodeVisitorFunc) { + fn(ancestors, n) + ancestors = append(ancestors, n) + for i := range n.children { + n.children[i].visitInOrder(ancestors, fn) + } +} + +var ( + indexVariables = "ijklmnop" + localVariables = "abcdefgh" +) + +// varsForDepth creates temporary variables guaranteed to be unique within lexical Go scopes +// of this depth in a function. It uses canonical Go loop variables for the first 7 levels +// and then resorts to uglier prefixes. +func varsForDepth(depth int) (index, local string) { + if depth > len(indexVariables) { + index = fmt.Sprintf("i%d", depth) + } else { + index = indexVariables[depth : depth+1] + } + if depth > len(localVariables) { + local = fmt.Sprintf("local%d", depth) + } else { + local = localVariables[depth : depth+1] + } + return +} + +// writeCalls generates a list of function calls based on the calls field for the provided variable +// name and pointer. +func (n *callNode) writeCalls(varName string, isVarPointer bool, sw *generator.SnippetWriter) { + accessor := varName + if !isVarPointer { + accessor = "&" + accessor + } + for _, fn := range n.call { + sw.Do("$.fn|raw$($.var$)\n", generator.Args{ + "fn": fn, + "var": accessor, + }) + } +} + +// WriteMethod performs an in-order traversal of the calltree, generating loops and if blocks as necessary +// to correctly turn the call tree into a method body that invokes all calls on all child nodes of the call tree. +// Depth is used to generate local variables at the proper depth. +func (n *callNode) WriteMethod(varName string, depth int, ancestors []*callNode, sw *generator.SnippetWriter) { + // if len(n.call) > 0 { + // sw.Do(fmt.Sprintf("// %s\n", callPath(append(ancestors, n)).String()), nil) + // } + + if len(n.field) > 0 { + varName = varName + "." + n.field + } + + index, local := varsForDepth(depth) + vars := generator.Args{ + "index": index, + "local": local, + "var": varName, + } + + isPointer := n.elem && !n.index + if isPointer && len(ancestors) > 0 { + sw.Do("if $.var$ != nil {\n", vars) + } + + switch { + case n.index: + sw.Do("for $.index$ := range $.var$ {\n", vars) + if n.elem { + sw.Do("$.local$ := $.var$[$.index$]\n", vars) + } else { + sw.Do("$.local$ := &$.var$[$.index$]\n", vars) + } + + n.writeCalls(local, true, sw) + for i := range n.children { + n.children[i].WriteMethod(local, depth+1, append(ancestors, n), sw) + } + sw.Do("}\n", nil) + case n.key: + default: + n.writeCalls(varName, isPointer, sw) + for i := range n.children { + n.children[i].WriteMethod(varName, depth, append(ancestors, n), sw) + } + } + + if isPointer && len(ancestors) > 0 { + sw.Do("}\n", nil) + } +} + +type callPath []*callNode + +// String prints a representation of a callPath that roughly approximates what a Go accessor +// would look like. Used for debugging only. +func (path callPath) String() string { + if len(path) == 0 { + return "" + } + var parts []string + for _, p := range path { + last := len(parts) - 1 + switch { + case p.elem: + if len(parts) > 0 { + parts[last] = "*" + parts[last] + } else { + parts = append(parts, "*") + } + case p.index: + if len(parts) > 0 { + parts[last] = parts[last] + "[i]" + } else { + parts = append(parts, "[i]") + } + case p.key: + if len(parts) > 0 { + parts[last] = parts[last] + "[key]" + } else { + parts = append(parts, "[key]") + } + default: + if len(p.field) > 0 { + parts = append(parts, p.field) + } else { + parts = append(parts, "") + } + } + } + var calls []string + for _, fn := range path[len(path)-1].call { + calls = append(calls, fn.Name.String()) + } + if len(calls) == 0 { + calls = append(calls, "") + } + + return strings.Join(parts, ".") + " calls " + strings.Join(calls, ", ") +} diff --git a/vendor/k8s.io/gengo/examples/set-gen/sets/byte.go b/vendor/k8s.io/gengo/examples/set-gen/sets/byte.go new file mode 100644 index 0000000000..766f4501e0 --- /dev/null +++ b/vendor/k8s.io/gengo/examples/set-gen/sets/byte.go @@ -0,0 +1,203 @@ +/* +Copyright 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. +*/ + +// Code generated by set-gen. DO NOT EDIT. + +package sets + +import ( + "reflect" + "sort" +) + +// sets.Byte is a set of bytes, implemented via map[byte]struct{} for minimal memory consumption. +type Byte map[byte]Empty + +// NewByte creates a Byte from a list of values. +func NewByte(items ...byte) Byte { + ss := Byte{} + ss.Insert(items...) + return ss +} + +// ByteKeySet creates a Byte from a keys of a map[byte](? extends interface{}). +// If the value passed in is not actually a map, this will panic. +func ByteKeySet(theMap interface{}) Byte { + v := reflect.ValueOf(theMap) + ret := Byte{} + + for _, keyValue := range v.MapKeys() { + ret.Insert(keyValue.Interface().(byte)) + } + return ret +} + +// Insert adds items to the set. +func (s Byte) Insert(items ...byte) { + for _, item := range items { + s[item] = Empty{} + } +} + +// Delete removes all items from the set. +func (s Byte) Delete(items ...byte) { + for _, item := range items { + delete(s, item) + } +} + +// Has returns true if and only if item is contained in the set. +func (s Byte) Has(item byte) bool { + _, contained := s[item] + return contained +} + +// HasAll returns true if and only if all items are contained in the set. +func (s Byte) HasAll(items ...byte) bool { + for _, item := range items { + if !s.Has(item) { + return false + } + } + return true +} + +// HasAny returns true if any items are contained in the set. +func (s Byte) HasAny(items ...byte) bool { + for _, item := range items { + if s.Has(item) { + return true + } + } + return false +} + +// Difference returns a set of objects that are not in s2 +// For example: +// s1 = {a1, a2, a3} +// s2 = {a1, a2, a4, a5} +// s1.Difference(s2) = {a3} +// s2.Difference(s1) = {a4, a5} +func (s Byte) Difference(s2 Byte) Byte { + result := NewByte() + for key := range s { + if !s2.Has(key) { + result.Insert(key) + } + } + return result +} + +// Union returns a new set which includes items in either s1 or s2. +// For example: +// s1 = {a1, a2} +// s2 = {a3, a4} +// s1.Union(s2) = {a1, a2, a3, a4} +// s2.Union(s1) = {a1, a2, a3, a4} +func (s1 Byte) Union(s2 Byte) Byte { + result := NewByte() + for key := range s1 { + result.Insert(key) + } + for key := range s2 { + result.Insert(key) + } + return result +} + +// Intersection returns a new set which includes the item in BOTH s1 and s2 +// For example: +// s1 = {a1, a2} +// s2 = {a2, a3} +// s1.Intersection(s2) = {a2} +func (s1 Byte) Intersection(s2 Byte) Byte { + var walk, other Byte + result := NewByte() + if s1.Len() < s2.Len() { + walk = s1 + other = s2 + } else { + walk = s2 + other = s1 + } + for key := range walk { + if other.Has(key) { + result.Insert(key) + } + } + return result +} + +// IsSuperset returns true if and only if s1 is a superset of s2. +func (s1 Byte) IsSuperset(s2 Byte) bool { + for item := range s2 { + if !s1.Has(item) { + return false + } + } + return true +} + +// Equal returns true if and only if s1 is equal (as a set) to s2. +// Two sets are equal if their membership is identical. +// (In practice, this means same elements, order doesn't matter) +func (s1 Byte) Equal(s2 Byte) bool { + return len(s1) == len(s2) && s1.IsSuperset(s2) +} + +type sortableSliceOfByte []byte + +func (s sortableSliceOfByte) Len() int { return len(s) } +func (s sortableSliceOfByte) Less(i, j int) bool { return lessByte(s[i], s[j]) } +func (s sortableSliceOfByte) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// List returns the contents as a sorted byte slice. +func (s Byte) List() []byte { + res := make(sortableSliceOfByte, 0, len(s)) + for key := range s { + res = append(res, key) + } + sort.Sort(res) + return []byte(res) +} + +// UnsortedList returns the slice with contents in random order. +func (s Byte) UnsortedList() []byte { + res := make([]byte, 0, len(s)) + for key := range s { + res = append(res, key) + } + return res +} + +// Returns a single element from the set. +func (s Byte) PopAny() (byte, bool) { + for key := range s { + s.Delete(key) + return key, true + } + var zeroValue byte + return zeroValue, false +} + +// Len returns the size of the set. +func (s Byte) Len() int { + return len(s) +} + +func lessByte(lhs, rhs byte) bool { + return lhs < rhs +} diff --git a/vendor/k8s.io/gengo/examples/set-gen/sets/doc.go b/vendor/k8s.io/gengo/examples/set-gen/sets/doc.go new file mode 100644 index 0000000000..b152a0bf00 --- /dev/null +++ b/vendor/k8s.io/gengo/examples/set-gen/sets/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 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. +*/ + +// Code generated by set-gen. DO NOT EDIT. + +// Package sets has auto-generated set types. +package sets diff --git a/vendor/k8s.io/gengo/examples/set-gen/sets/empty.go b/vendor/k8s.io/gengo/examples/set-gen/sets/empty.go new file mode 100644 index 0000000000..e11e622c5b --- /dev/null +++ b/vendor/k8s.io/gengo/examples/set-gen/sets/empty.go @@ -0,0 +1,23 @@ +/* +Copyright 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. +*/ + +// Code generated by set-gen. DO NOT EDIT. + +package sets + +// Empty is public since it is used by some internal API objects for conversions between external +// string arrays and internal sets, and conversion logic requires public types today. +type Empty struct{} diff --git a/vendor/k8s.io/gengo/examples/set-gen/sets/int.go b/vendor/k8s.io/gengo/examples/set-gen/sets/int.go new file mode 100644 index 0000000000..a0a513cd9b --- /dev/null +++ b/vendor/k8s.io/gengo/examples/set-gen/sets/int.go @@ -0,0 +1,203 @@ +/* +Copyright 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. +*/ + +// Code generated by set-gen. DO NOT EDIT. + +package sets + +import ( + "reflect" + "sort" +) + +// sets.Int is a set of ints, implemented via map[int]struct{} for minimal memory consumption. +type Int map[int]Empty + +// NewInt creates a Int from a list of values. +func NewInt(items ...int) Int { + ss := Int{} + ss.Insert(items...) + return ss +} + +// IntKeySet creates a Int from a keys of a map[int](? extends interface{}). +// If the value passed in is not actually a map, this will panic. +func IntKeySet(theMap interface{}) Int { + v := reflect.ValueOf(theMap) + ret := Int{} + + for _, keyValue := range v.MapKeys() { + ret.Insert(keyValue.Interface().(int)) + } + return ret +} + +// Insert adds items to the set. +func (s Int) Insert(items ...int) { + for _, item := range items { + s[item] = Empty{} + } +} + +// Delete removes all items from the set. +func (s Int) Delete(items ...int) { + for _, item := range items { + delete(s, item) + } +} + +// Has returns true if and only if item is contained in the set. +func (s Int) Has(item int) bool { + _, contained := s[item] + return contained +} + +// HasAll returns true if and only if all items are contained in the set. +func (s Int) HasAll(items ...int) bool { + for _, item := range items { + if !s.Has(item) { + return false + } + } + return true +} + +// HasAny returns true if any items are contained in the set. +func (s Int) HasAny(items ...int) bool { + for _, item := range items { + if s.Has(item) { + return true + } + } + return false +} + +// Difference returns a set of objects that are not in s2 +// For example: +// s1 = {a1, a2, a3} +// s2 = {a1, a2, a4, a5} +// s1.Difference(s2) = {a3} +// s2.Difference(s1) = {a4, a5} +func (s Int) Difference(s2 Int) Int { + result := NewInt() + for key := range s { + if !s2.Has(key) { + result.Insert(key) + } + } + return result +} + +// Union returns a new set which includes items in either s1 or s2. +// For example: +// s1 = {a1, a2} +// s2 = {a3, a4} +// s1.Union(s2) = {a1, a2, a3, a4} +// s2.Union(s1) = {a1, a2, a3, a4} +func (s1 Int) Union(s2 Int) Int { + result := NewInt() + for key := range s1 { + result.Insert(key) + } + for key := range s2 { + result.Insert(key) + } + return result +} + +// Intersection returns a new set which includes the item in BOTH s1 and s2 +// For example: +// s1 = {a1, a2} +// s2 = {a2, a3} +// s1.Intersection(s2) = {a2} +func (s1 Int) Intersection(s2 Int) Int { + var walk, other Int + result := NewInt() + if s1.Len() < s2.Len() { + walk = s1 + other = s2 + } else { + walk = s2 + other = s1 + } + for key := range walk { + if other.Has(key) { + result.Insert(key) + } + } + return result +} + +// IsSuperset returns true if and only if s1 is a superset of s2. +func (s1 Int) IsSuperset(s2 Int) bool { + for item := range s2 { + if !s1.Has(item) { + return false + } + } + return true +} + +// Equal returns true if and only if s1 is equal (as a set) to s2. +// Two sets are equal if their membership is identical. +// (In practice, this means same elements, order doesn't matter) +func (s1 Int) Equal(s2 Int) bool { + return len(s1) == len(s2) && s1.IsSuperset(s2) +} + +type sortableSliceOfInt []int + +func (s sortableSliceOfInt) Len() int { return len(s) } +func (s sortableSliceOfInt) Less(i, j int) bool { return lessInt(s[i], s[j]) } +func (s sortableSliceOfInt) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// List returns the contents as a sorted int slice. +func (s Int) List() []int { + res := make(sortableSliceOfInt, 0, len(s)) + for key := range s { + res = append(res, key) + } + sort.Sort(res) + return []int(res) +} + +// UnsortedList returns the slice with contents in random order. +func (s Int) UnsortedList() []int { + res := make([]int, 0, len(s)) + for key := range s { + res = append(res, key) + } + return res +} + +// Returns a single element from the set. +func (s Int) PopAny() (int, bool) { + for key := range s { + s.Delete(key) + return key, true + } + var zeroValue int + return zeroValue, false +} + +// Len returns the size of the set. +func (s Int) Len() int { + return len(s) +} + +func lessInt(lhs, rhs int) bool { + return lhs < rhs +} diff --git a/vendor/k8s.io/gengo/examples/set-gen/sets/int64.go b/vendor/k8s.io/gengo/examples/set-gen/sets/int64.go new file mode 100644 index 0000000000..9ca9af0c59 --- /dev/null +++ b/vendor/k8s.io/gengo/examples/set-gen/sets/int64.go @@ -0,0 +1,203 @@ +/* +Copyright 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. +*/ + +// Code generated by set-gen. DO NOT EDIT. + +package sets + +import ( + "reflect" + "sort" +) + +// sets.Int64 is a set of int64s, implemented via map[int64]struct{} for minimal memory consumption. +type Int64 map[int64]Empty + +// NewInt64 creates a Int64 from a list of values. +func NewInt64(items ...int64) Int64 { + ss := Int64{} + ss.Insert(items...) + return ss +} + +// Int64KeySet creates a Int64 from a keys of a map[int64](? extends interface{}). +// If the value passed in is not actually a map, this will panic. +func Int64KeySet(theMap interface{}) Int64 { + v := reflect.ValueOf(theMap) + ret := Int64{} + + for _, keyValue := range v.MapKeys() { + ret.Insert(keyValue.Interface().(int64)) + } + return ret +} + +// Insert adds items to the set. +func (s Int64) Insert(items ...int64) { + for _, item := range items { + s[item] = Empty{} + } +} + +// Delete removes all items from the set. +func (s Int64) Delete(items ...int64) { + for _, item := range items { + delete(s, item) + } +} + +// Has returns true if and only if item is contained in the set. +func (s Int64) Has(item int64) bool { + _, contained := s[item] + return contained +} + +// HasAll returns true if and only if all items are contained in the set. +func (s Int64) HasAll(items ...int64) bool { + for _, item := range items { + if !s.Has(item) { + return false + } + } + return true +} + +// HasAny returns true if any items are contained in the set. +func (s Int64) HasAny(items ...int64) bool { + for _, item := range items { + if s.Has(item) { + return true + } + } + return false +} + +// Difference returns a set of objects that are not in s2 +// For example: +// s1 = {a1, a2, a3} +// s2 = {a1, a2, a4, a5} +// s1.Difference(s2) = {a3} +// s2.Difference(s1) = {a4, a5} +func (s Int64) Difference(s2 Int64) Int64 { + result := NewInt64() + for key := range s { + if !s2.Has(key) { + result.Insert(key) + } + } + return result +} + +// Union returns a new set which includes items in either s1 or s2. +// For example: +// s1 = {a1, a2} +// s2 = {a3, a4} +// s1.Union(s2) = {a1, a2, a3, a4} +// s2.Union(s1) = {a1, a2, a3, a4} +func (s1 Int64) Union(s2 Int64) Int64 { + result := NewInt64() + for key := range s1 { + result.Insert(key) + } + for key := range s2 { + result.Insert(key) + } + return result +} + +// Intersection returns a new set which includes the item in BOTH s1 and s2 +// For example: +// s1 = {a1, a2} +// s2 = {a2, a3} +// s1.Intersection(s2) = {a2} +func (s1 Int64) Intersection(s2 Int64) Int64 { + var walk, other Int64 + result := NewInt64() + if s1.Len() < s2.Len() { + walk = s1 + other = s2 + } else { + walk = s2 + other = s1 + } + for key := range walk { + if other.Has(key) { + result.Insert(key) + } + } + return result +} + +// IsSuperset returns true if and only if s1 is a superset of s2. +func (s1 Int64) IsSuperset(s2 Int64) bool { + for item := range s2 { + if !s1.Has(item) { + return false + } + } + return true +} + +// Equal returns true if and only if s1 is equal (as a set) to s2. +// Two sets are equal if their membership is identical. +// (In practice, this means same elements, order doesn't matter) +func (s1 Int64) Equal(s2 Int64) bool { + return len(s1) == len(s2) && s1.IsSuperset(s2) +} + +type sortableSliceOfInt64 []int64 + +func (s sortableSliceOfInt64) Len() int { return len(s) } +func (s sortableSliceOfInt64) Less(i, j int) bool { return lessInt64(s[i], s[j]) } +func (s sortableSliceOfInt64) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// List returns the contents as a sorted int64 slice. +func (s Int64) List() []int64 { + res := make(sortableSliceOfInt64, 0, len(s)) + for key := range s { + res = append(res, key) + } + sort.Sort(res) + return []int64(res) +} + +// UnsortedList returns the slice with contents in random order. +func (s Int64) UnsortedList() []int64 { + res := make([]int64, 0, len(s)) + for key := range s { + res = append(res, key) + } + return res +} + +// Returns a single element from the set. +func (s Int64) PopAny() (int64, bool) { + for key := range s { + s.Delete(key) + return key, true + } + var zeroValue int64 + return zeroValue, false +} + +// Len returns the size of the set. +func (s Int64) Len() int { + return len(s) +} + +func lessInt64(lhs, rhs int64) bool { + return lhs < rhs +} diff --git a/vendor/k8s.io/gengo/examples/set-gen/sets/string.go b/vendor/k8s.io/gengo/examples/set-gen/sets/string.go new file mode 100644 index 0000000000..ba00ad7df4 --- /dev/null +++ b/vendor/k8s.io/gengo/examples/set-gen/sets/string.go @@ -0,0 +1,203 @@ +/* +Copyright 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. +*/ + +// Code generated by set-gen. DO NOT EDIT. + +package sets + +import ( + "reflect" + "sort" +) + +// sets.String is a set of strings, implemented via map[string]struct{} for minimal memory consumption. +type String map[string]Empty + +// NewString creates a String from a list of values. +func NewString(items ...string) String { + ss := String{} + ss.Insert(items...) + return ss +} + +// StringKeySet creates a String from a keys of a map[string](? extends interface{}). +// If the value passed in is not actually a map, this will panic. +func StringKeySet(theMap interface{}) String { + v := reflect.ValueOf(theMap) + ret := String{} + + for _, keyValue := range v.MapKeys() { + ret.Insert(keyValue.Interface().(string)) + } + return ret +} + +// Insert adds items to the set. +func (s String) Insert(items ...string) { + for _, item := range items { + s[item] = Empty{} + } +} + +// Delete removes all items from the set. +func (s String) Delete(items ...string) { + for _, item := range items { + delete(s, item) + } +} + +// Has returns true if and only if item is contained in the set. +func (s String) Has(item string) bool { + _, contained := s[item] + return contained +} + +// HasAll returns true if and only if all items are contained in the set. +func (s String) HasAll(items ...string) bool { + for _, item := range items { + if !s.Has(item) { + return false + } + } + return true +} + +// HasAny returns true if any items are contained in the set. +func (s String) HasAny(items ...string) bool { + for _, item := range items { + if s.Has(item) { + return true + } + } + return false +} + +// Difference returns a set of objects that are not in s2 +// For example: +// s1 = {a1, a2, a3} +// s2 = {a1, a2, a4, a5} +// s1.Difference(s2) = {a3} +// s2.Difference(s1) = {a4, a5} +func (s String) Difference(s2 String) String { + result := NewString() + for key := range s { + if !s2.Has(key) { + result.Insert(key) + } + } + return result +} + +// Union returns a new set which includes items in either s1 or s2. +// For example: +// s1 = {a1, a2} +// s2 = {a3, a4} +// s1.Union(s2) = {a1, a2, a3, a4} +// s2.Union(s1) = {a1, a2, a3, a4} +func (s1 String) Union(s2 String) String { + result := NewString() + for key := range s1 { + result.Insert(key) + } + for key := range s2 { + result.Insert(key) + } + return result +} + +// Intersection returns a new set which includes the item in BOTH s1 and s2 +// For example: +// s1 = {a1, a2} +// s2 = {a2, a3} +// s1.Intersection(s2) = {a2} +func (s1 String) Intersection(s2 String) String { + var walk, other String + result := NewString() + if s1.Len() < s2.Len() { + walk = s1 + other = s2 + } else { + walk = s2 + other = s1 + } + for key := range walk { + if other.Has(key) { + result.Insert(key) + } + } + return result +} + +// IsSuperset returns true if and only if s1 is a superset of s2. +func (s1 String) IsSuperset(s2 String) bool { + for item := range s2 { + if !s1.Has(item) { + return false + } + } + return true +} + +// Equal returns true if and only if s1 is equal (as a set) to s2. +// Two sets are equal if their membership is identical. +// (In practice, this means same elements, order doesn't matter) +func (s1 String) Equal(s2 String) bool { + return len(s1) == len(s2) && s1.IsSuperset(s2) +} + +type sortableSliceOfString []string + +func (s sortableSliceOfString) Len() int { return len(s) } +func (s sortableSliceOfString) Less(i, j int) bool { return lessString(s[i], s[j]) } +func (s sortableSliceOfString) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// List returns the contents as a sorted string slice. +func (s String) List() []string { + res := make(sortableSliceOfString, 0, len(s)) + for key := range s { + res = append(res, key) + } + sort.Sort(res) + return []string(res) +} + +// UnsortedList returns the slice with contents in random order. +func (s String) UnsortedList() []string { + res := make([]string, 0, len(s)) + for key := range s { + res = append(res, key) + } + return res +} + +// Returns a single element from the set. +func (s String) PopAny() (string, bool) { + for key := range s { + s.Delete(key) + return key, true + } + var zeroValue string + return zeroValue, false +} + +// Len returns the size of the set. +func (s String) Len() int { + return len(s) +} + +func lessString(lhs, rhs string) bool { + return lhs < rhs +} diff --git a/vendor/k8s.io/gengo/generator/default_generator.go b/vendor/k8s.io/gengo/generator/default_generator.go new file mode 100644 index 0000000000..f947668214 --- /dev/null +++ b/vendor/k8s.io/gengo/generator/default_generator.go @@ -0,0 +1,62 @@ +/* +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 generator + +import ( + "io" + + "k8s.io/gengo/namer" + "k8s.io/gengo/types" +) + +const ( + GolangFileType = "golang" +) + +// DefaultGen implements a do-nothing Generator. +// +// It can be used to implement static content files. +type DefaultGen struct { + // OptionalName, if present, will be used for the generator's name, and + // the filename (with ".go" appended). + OptionalName string + + // OptionalBody, if present, will be used as the return from the "Init" + // method. This causes it to be static content for the entire file if + // no other generator touches the file. + OptionalBody []byte +} + +func (d DefaultGen) Name() string { return d.OptionalName } +func (d DefaultGen) Filter(*Context, *types.Type) bool { return true } +func (d DefaultGen) Namers(*Context) namer.NameSystems { return nil } +func (d DefaultGen) Imports(*Context) []string { return []string{} } +func (d DefaultGen) PackageVars(*Context) []string { return []string{} } +func (d DefaultGen) PackageConsts(*Context) []string { return []string{} } +func (d DefaultGen) GenerateType(*Context, *types.Type, io.Writer) error { return nil } +func (d DefaultGen) Filename() string { return d.OptionalName + ".go" } +func (d DefaultGen) FileType() string { return GolangFileType } +func (d DefaultGen) Finalize(*Context, io.Writer) error { return nil } + +func (d DefaultGen) Init(c *Context, w io.Writer) error { + _, err := w.Write(d.OptionalBody) + return err +} + +var ( + _ = Generator(DefaultGen{}) +) diff --git a/vendor/k8s.io/gengo/generator/default_package.go b/vendor/k8s.io/gengo/generator/default_package.go new file mode 100644 index 0000000000..11517fc6ae --- /dev/null +++ b/vendor/k8s.io/gengo/generator/default_package.go @@ -0,0 +1,72 @@ +/* +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 generator + +import ( + "k8s.io/gengo/types" +) + +// DefaultPackage contains a default implementation of Package. +type DefaultPackage struct { + // Short name of package, used in the "package xxxx" line. + PackageName string + // Import path of the package, and the location on disk of the package. + PackagePath string + + // Emitted at the top of every file. + HeaderText []byte + + // Emitted only for a "doc.go" file; appended to the HeaderText for + // that file. + PackageDocumentation []byte + + // If non-nil, will be called on "Generators"; otherwise, the static + // list will be used. So you should set only one of these two fields. + GeneratorFunc func(*Context) []Generator + GeneratorList []Generator + + // Optional; filters the types exposed to the generators. + FilterFunc func(*Context, *types.Type) bool +} + +func (d *DefaultPackage) Name() string { return d.PackageName } +func (d *DefaultPackage) Path() string { return d.PackagePath } + +func (d *DefaultPackage) Filter(c *Context, t *types.Type) bool { + if d.FilterFunc != nil { + return d.FilterFunc(c, t) + } + return true +} + +func (d *DefaultPackage) Generators(c *Context) []Generator { + if d.GeneratorFunc != nil { + return d.GeneratorFunc(c) + } + return d.GeneratorList +} + +func (d *DefaultPackage) Header(filename string) []byte { + if filename == "doc.go" { + return append(d.HeaderText, d.PackageDocumentation...) + } + return d.HeaderText +} + +var ( + _ = Package(&DefaultPackage{}) +) diff --git a/vendor/k8s.io/gengo/generator/doc.go b/vendor/k8s.io/gengo/generator/doc.go new file mode 100644 index 0000000000..d8e12534a4 --- /dev/null +++ b/vendor/k8s.io/gengo/generator/doc.go @@ -0,0 +1,31 @@ +/* +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 generator defines an interface for code generators to implement. +// +// To use this package, you'll implement the "Package" and "Generator" +// interfaces; you'll call NewContext to load up the types you want to work +// with, and then you'll call one or more of the Execute methods. See the +// interface definitions for explanations. All output will have gofmt called on +// it automatically, so you do not need to worry about generating correct +// indentation. +// +// This package also exposes SnippetWriter. SnippetWriter reduces to a minimum +// the boilerplate involved in setting up a template from go's text/template +// package. Additionally, all naming systems in the Context will be added as +// functions to the parsed template, so that they can be called directly from +// your templates! +package generator // import "k8s.io/gengo/generator" diff --git a/vendor/k8s.io/gengo/generator/error_tracker.go b/vendor/k8s.io/gengo/generator/error_tracker.go new file mode 100644 index 0000000000..964dae37ba --- /dev/null +++ b/vendor/k8s.io/gengo/generator/error_tracker.go @@ -0,0 +1,50 @@ +/* +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 generator + +import ( + "io" +) + +// ErrorTracker tracks errors to the underlying writer, so that you can ignore +// them until you're ready to return. +type ErrorTracker struct { + io.Writer + err error +} + +// NewErrorTracker makes a new error tracker; note that it implements io.Writer. +func NewErrorTracker(w io.Writer) *ErrorTracker { + return &ErrorTracker{Writer: w} +} + +// Write intercepts calls to Write. +func (et *ErrorTracker) Write(p []byte) (n int, err error) { + if et.err != nil { + return 0, et.err + } + n, err = et.Writer.Write(p) + if err != nil { + et.err = err + } + return n, err +} + +// Error returns nil if no error has occurred, otherwise it returns the error. +func (et *ErrorTracker) Error() error { + return et.err +} diff --git a/vendor/k8s.io/gengo/generator/execute.go b/vendor/k8s.io/gengo/generator/execute.go new file mode 100644 index 0000000000..b5f5aaeb44 --- /dev/null +++ b/vendor/k8s.io/gengo/generator/execute.go @@ -0,0 +1,312 @@ +/* +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 generator + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "golang.org/x/tools/imports" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + + "k8s.io/klog" +) + +func errs2strings(errors []error) []string { + strs := make([]string, len(errors)) + for i := range errors { + strs[i] = errors[i].Error() + } + return strs +} + +// ExecutePackages runs the generators for every package in 'packages'. 'outDir' +// is the base directory in which to place all the generated packages; it +// should be a physical path on disk, not an import path. e.g.: +// /path/to/home/path/to/gopath/src/ +// Each package has its import path already, this will be appended to 'outDir'. +func (c *Context) ExecutePackages(outDir string, packages Packages) error { + var errors []error + for _, p := range packages { + if err := c.ExecutePackage(outDir, p); err != nil { + errors = append(errors, err) + } + } + if len(errors) > 0 { + return fmt.Errorf("some packages had errors:\n%v\n", strings.Join(errs2strings(errors), "\n")) + } + return nil +} + +type DefaultFileType struct { + Format func([]byte) ([]byte, error) + Assemble func(io.Writer, *File) +} + +func (ft DefaultFileType) AssembleFile(f *File, pathname string) error { + klog.V(2).Infof("Assembling file %q", pathname) + destFile, err := os.Create(pathname) + if err != nil { + return err + } + defer destFile.Close() + + b := &bytes.Buffer{} + et := NewErrorTracker(b) + ft.Assemble(et, f) + if et.Error() != nil { + return et.Error() + } + if formatted, err := ft.Format(b.Bytes()); err != nil { + err = fmt.Errorf("unable to format file %q (%v).", pathname, err) + // Write the file anyway, so they can see what's going wrong and fix the generator. + if _, err2 := destFile.Write(b.Bytes()); err2 != nil { + return err2 + } + return err + } else { + _, err = destFile.Write(formatted) + return err + } +} + +func (ft DefaultFileType) VerifyFile(f *File, pathname string) error { + klog.V(2).Infof("Verifying file %q", pathname) + friendlyName := filepath.Join(f.PackageName, f.Name) + b := &bytes.Buffer{} + et := NewErrorTracker(b) + ft.Assemble(et, f) + if et.Error() != nil { + return et.Error() + } + formatted, err := ft.Format(b.Bytes()) + if err != nil { + return fmt.Errorf("unable to format the output for %q: %v", friendlyName, err) + } + existing, err := ioutil.ReadFile(pathname) + if err != nil { + return fmt.Errorf("unable to read file %q for comparison: %v", friendlyName, err) + } + if bytes.Compare(formatted, existing) == 0 { + return nil + } + // Be nice and find the first place where they differ + i := 0 + for i < len(formatted) && i < len(existing) && formatted[i] == existing[i] { + i++ + } + eDiff, fDiff := existing[i:], formatted[i:] + if len(eDiff) > 100 { + eDiff = eDiff[:100] + } + if len(fDiff) > 100 { + fDiff = fDiff[:100] + } + return fmt.Errorf("output for %q differs; first existing/expected diff: \n %q\n %q", friendlyName, string(eDiff), string(fDiff)) +} + +func assembleGolangFile(w io.Writer, f *File) { + w.Write(f.Header) + fmt.Fprintf(w, "package %v\n\n", f.PackageName) + + if len(f.Imports) > 0 { + fmt.Fprint(w, "import (\n") + for i := range f.Imports { + if strings.Contains(i, "\"") { + // they included quotes, or are using the + // `name "path/to/pkg"` format. + fmt.Fprintf(w, "\t%s\n", i) + } else { + fmt.Fprintf(w, "\t%q\n", i) + } + } + fmt.Fprint(w, ")\n\n") + } + + if f.Vars.Len() > 0 { + fmt.Fprint(w, "var (\n") + w.Write(f.Vars.Bytes()) + fmt.Fprint(w, ")\n\n") + } + + if f.Consts.Len() > 0 { + fmt.Fprint(w, "const (\n") + w.Write(f.Consts.Bytes()) + fmt.Fprint(w, ")\n\n") + } + + w.Write(f.Body.Bytes()) +} + +func importsWrapper(src []byte) ([]byte, error) { + return imports.Process("", src, nil) +} + +func NewGolangFile() *DefaultFileType { + return &DefaultFileType{ + Format: importsWrapper, + Assemble: assembleGolangFile, + } +} + +// format should be one line only, and not end with \n. +func addIndentHeaderComment(b *bytes.Buffer, format string, args ...interface{}) { + if b.Len() > 0 { + fmt.Fprintf(b, "\n// "+format+"\n", args...) + } else { + fmt.Fprintf(b, "// "+format+"\n", args...) + } +} + +func (c *Context) filteredBy(f func(*Context, *types.Type) bool) *Context { + c2 := *c + c2.Order = []*types.Type{} + for _, t := range c.Order { + if f(c, t) { + c2.Order = append(c2.Order, t) + } + } + return &c2 +} + +// make a new context; inheret c.Namers, but add on 'namers'. In case of a name +// collision, the namer in 'namers' wins. +func (c *Context) addNameSystems(namers namer.NameSystems) *Context { + if namers == nil { + return c + } + c2 := *c + // Copy the existing name systems so we don't corrupt a parent context + c2.Namers = namer.NameSystems{} + for k, v := range c.Namers { + c2.Namers[k] = v + } + + for name, namer := range namers { + c2.Namers[name] = namer + } + return &c2 +} + +// ExecutePackage executes a single package. 'outDir' is the base directory in +// which to place the package; it should be a physical path on disk, not an +// import path. e.g.: '/path/to/home/path/to/gopath/src/' The package knows its +// import path already, this will be appended to 'outDir'. +func (c *Context) ExecutePackage(outDir string, p Package) error { + path := filepath.Join(outDir, p.Path()) + klog.V(2).Infof("Processing package %q, disk location %q", p.Name(), path) + // Filter out any types the *package* doesn't care about. + packageContext := c.filteredBy(p.Filter) + os.MkdirAll(path, 0755) + files := map[string]*File{} + for _, g := range p.Generators(packageContext) { + // Filter out types the *generator* doesn't care about. + genContext := packageContext.filteredBy(g.Filter) + // Now add any extra name systems defined by this generator + genContext = genContext.addNameSystems(g.Namers(genContext)) + + fileType := g.FileType() + if len(fileType) == 0 { + return fmt.Errorf("generator %q must specify a file type", g.Name()) + } + f := files[g.Filename()] + if f == nil { + // This is the first generator to reference this file, so start it. + f = &File{ + Name: g.Filename(), + FileType: fileType, + PackageName: p.Name(), + Header: p.Header(g.Filename()), + Imports: map[string]struct{}{}, + } + files[f.Name] = f + } else { + if f.FileType != g.FileType() { + return fmt.Errorf("file %q already has type %q, but generator %q wants to use type %q", f.Name, f.FileType, g.Name(), g.FileType()) + } + } + + if vars := g.PackageVars(genContext); len(vars) > 0 { + addIndentHeaderComment(&f.Vars, "Package-wide variables from generator %q.", g.Name()) + for _, v := range vars { + if _, err := fmt.Fprintf(&f.Vars, "%s\n", v); err != nil { + return err + } + } + } + if consts := g.PackageConsts(genContext); len(consts) > 0 { + addIndentHeaderComment(&f.Consts, "Package-wide consts from generator %q.", g.Name()) + for _, v := range consts { + if _, err := fmt.Fprintf(&f.Consts, "%s\n", v); err != nil { + return err + } + } + } + if err := genContext.executeBody(&f.Body, g); err != nil { + return err + } + if imports := g.Imports(genContext); len(imports) > 0 { + for _, i := range imports { + f.Imports[i] = struct{}{} + } + } + } + + var errors []error + for _, f := range files { + finalPath := filepath.Join(path, f.Name) + assembler, ok := c.FileTypes[f.FileType] + if !ok { + return fmt.Errorf("the file type %q registered for file %q does not exist in the context", f.FileType, f.Name) + } + var err error + if c.Verify { + err = assembler.VerifyFile(f, finalPath) + } else { + err = assembler.AssembleFile(f, finalPath) + } + if err != nil { + errors = append(errors, err) + } + } + if len(errors) > 0 { + return fmt.Errorf("errors in package %q:\n%v\n", p.Path(), strings.Join(errs2strings(errors), "\n")) + } + return nil +} + +func (c *Context) executeBody(w io.Writer, generator Generator) error { + et := NewErrorTracker(w) + if err := generator.Init(c, et); err != nil { + return err + } + for _, t := range c.Order { + if err := generator.GenerateType(c, t, et); err != nil { + return err + } + } + if err := generator.Finalize(c, et); err != nil { + return err + } + return et.Error() +} diff --git a/vendor/k8s.io/gengo/generator/generator.go b/vendor/k8s.io/gengo/generator/generator.go new file mode 100644 index 0000000000..05a3f65fe8 --- /dev/null +++ b/vendor/k8s.io/gengo/generator/generator.go @@ -0,0 +1,219 @@ +/* +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 generator + +import ( + "bytes" + "io" + + "k8s.io/gengo/namer" + "k8s.io/gengo/parser" + "k8s.io/gengo/types" +) + +// Package contains the contract for generating a package. +type Package interface { + // Name returns the package short name. + Name() string + // Path returns the package import path. + Path() string + + // Filter should return true if this package cares about this type. + // Otherwise, this type will be omitted from the type ordering for + // this package. + Filter(*Context, *types.Type) bool + + // Header should return a header for the file, including comment markers. + // Useful for copyright notices and doc strings. Include an + // autogeneration notice! Do not include the "package x" line. + Header(filename string) []byte + + // Generators returns the list of generators for this package. It is + // allowed for more than one generator to write to the same file. + // A Context is passed in case the list of generators depends on the + // input types. + Generators(*Context) []Generator +} + +type File struct { + Name string + FileType string + PackageName string + Header []byte + Imports map[string]struct{} + Vars bytes.Buffer + Consts bytes.Buffer + Body bytes.Buffer +} + +type FileType interface { + AssembleFile(f *File, path string) error + VerifyFile(f *File, path string) error +} + +// Packages is a list of packages to generate. +type Packages []Package + +// Generator is the contract for anything that wants to do auto-generation. +// It's expected that the io.Writers passed to the below functions will be +// ErrorTrackers; this allows implementations to not check for io errors, +// making more readable code. +// +// The call order for the functions that take a Context is: +// 1. Filter() // Subsequent calls see only types that pass this. +// 2. Namers() // Subsequent calls see the namers provided by this. +// 3. PackageVars() +// 4. PackageConsts() +// 5. Init() +// 6. GenerateType() // Called N times, once per type in the context's Order. +// 7. Imports() +// +// You may have multiple generators for the same file. +type Generator interface { + // The name of this generator. Will be included in generated comments. + Name() string + + // Filter should return true if this generator cares about this type. + // (otherwise, GenerateType will not be called.) + // + // Filter is called before any of the generator's other functions; + // subsequent calls will get a context with only the types that passed + // this filter. + Filter(*Context, *types.Type) bool + + // If this generator needs special namers, return them here. These will + // override the original namers in the context if there is a collision. + // You may return nil if you don't need special names. These names will + // be available in the context passed to the rest of the generator's + // functions. + // + // A use case for this is to return a namer that tracks imports. + Namers(*Context) namer.NameSystems + + // Init should write an init function, and any other content that's not + // generated per-type. (It's not intended for generator specific + // initialization! Do that when your Package constructs the + // Generators.) + Init(*Context, io.Writer) error + + // Finalize should write finish up functions, and any other content that's not + // generated per-type. + Finalize(*Context, io.Writer) error + + // PackageVars should emit an array of variable lines. They will be + // placed in a var ( ... ) block. There's no need to include a leading + // \t or trailing \n. + PackageVars(*Context) []string + + // PackageConsts should emit an array of constant lines. They will be + // placed in a const ( ... ) block. There's no need to include a leading + // \t or trailing \n. + PackageConsts(*Context) []string + + // GenerateType should emit the code for a particular type. + GenerateType(*Context, *types.Type, io.Writer) error + + // Imports should return a list of necessary imports. They will be + // formatted correctly. You do not need to include quotation marks, + // return only the package name; alternatively, you can also return + // imports in the format `name "path/to/pkg"`. Imports will be called + // after Init, PackageVars, PackageConsts, and GenerateType, to allow + // you to keep track of what imports you actually need. + Imports(*Context) []string + + // Preferred file name of this generator, not including a path. It is + // allowed for multiple generators to use the same filename, but it's + // up to you to make sure they don't have colliding import names. + // TODO: provide per-file import tracking, removing the requirement + // that generators coordinate.. + Filename() string + + // A registered file type in the context to generate this file with. If + // the FileType is not found in the context, execution will stop. + FileType() string +} + +// Context is global context for individual generators to consume. +type Context struct { + // A map from the naming system to the names for that system. E.g., you + // might have public names and several private naming systems. + Namers namer.NameSystems + + // All the types, in case you want to look up something. + Universe types.Universe + + // All the user-specified packages. This is after recursive expansion. + Inputs []string + + // The canonical ordering of the types (will be filtered by both the + // Package's and Generator's Filter methods). + Order []*types.Type + + // A set of types this context can process. If this is empty or nil, + // the default "golang" filetype will be provided. + FileTypes map[string]FileType + + // If true, Execute* calls will just verify that the existing output is + // correct. (You may set this after calling NewContext.) + Verify bool + + // Allows generators to add packages at runtime. + builder *parser.Builder +} + +// NewContext generates a context from the given builder, naming systems, and +// the naming system you wish to construct the canonical ordering from. +func NewContext(b *parser.Builder, nameSystems namer.NameSystems, canonicalOrderName string) (*Context, error) { + universe, err := b.FindTypes() + if err != nil { + return nil, err + } + + c := &Context{ + Namers: namer.NameSystems{}, + Universe: universe, + Inputs: b.FindPackages(), + FileTypes: map[string]FileType{ + GolangFileType: NewGolangFile(), + }, + builder: b, + } + + for name, systemNamer := range nameSystems { + c.Namers[name] = systemNamer + if name == canonicalOrderName { + orderer := namer.Orderer{Namer: systemNamer} + c.Order = orderer.OrderUniverse(universe) + } + } + return c, nil +} + +// AddDir adds a Go package to the context. The specified path must be a single +// go package import path. GOPATH, GOROOT, and the location of your go binary +// (`which go`) will all be searched, in the normal Go fashion. +// Deprecated. Please use AddDirectory. +func (ctxt *Context) AddDir(path string) error { + return ctxt.builder.AddDirTo(path, &ctxt.Universe) +} + +// AddDirectory adds a Go package to the context. The specified path must be a +// single go package import path. GOPATH, GOROOT, and the location of your go +// binary (`which go`) will all be searched, in the normal Go fashion. +func (ctxt *Context) AddDirectory(path string) (*types.Package, error) { + return ctxt.builder.AddDirectoryTo(path, &ctxt.Universe) +} diff --git a/vendor/k8s.io/gengo/generator/import_tracker.go b/vendor/k8s.io/gengo/generator/import_tracker.go new file mode 100644 index 0000000000..5d058410ac --- /dev/null +++ b/vendor/k8s.io/gengo/generator/import_tracker.go @@ -0,0 +1,64 @@ +/* +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 generator + +import ( + "strings" + + "k8s.io/klog" + + "k8s.io/gengo/namer" + "k8s.io/gengo/types" +) + +func NewImportTracker(typesToAdd ...*types.Type) namer.ImportTracker { + tracker := namer.NewDefaultImportTracker(types.Name{}) + tracker.IsInvalidType = func(*types.Type) bool { return false } + tracker.LocalName = func(name types.Name) string { return golangTrackerLocalName(&tracker, name) } + tracker.PrintImport = func(path, name string) string { return name + " \"" + path + "\"" } + + tracker.AddTypes(typesToAdd...) + return &tracker + +} + +func golangTrackerLocalName(tracker namer.ImportTracker, t types.Name) string { + path := t.Package + + // Using backslashes in package names causes gengo to produce Go code which + // will not compile with the gc compiler. See the comment on GoSeperator. + if strings.ContainsRune(path, '\\') { + klog.Warningf("Warning: backslash used in import path '%v', this is unsupported.\n", path) + } + + dirs := strings.Split(path, namer.GoSeperator) + for n := len(dirs) - 1; n >= 0; n-- { + // follow kube convention of not having anything between directory names + name := strings.Join(dirs[n:], "") + name = strings.Replace(name, "_", "", -1) + // These characters commonly appear in import paths for go + // packages, but aren't legal go names. So we'll sanitize. + name = strings.Replace(name, ".", "", -1) + name = strings.Replace(name, "-", "", -1) + if _, found := tracker.PathOf(name); found { + // This name collides with some other package + continue + } + return name + } + panic("can't find import for " + path) +} diff --git a/vendor/k8s.io/gengo/generator/snippet_writer.go b/vendor/k8s.io/gengo/generator/snippet_writer.go new file mode 100644 index 0000000000..eae917c138 --- /dev/null +++ b/vendor/k8s.io/gengo/generator/snippet_writer.go @@ -0,0 +1,154 @@ +/* +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 generator + +import ( + "fmt" + "io" + "runtime" + "text/template" +) + +// SnippetWriter is an attempt to make the template library usable. +// Methods are chainable, and you don't have to check Error() until you're all +// done. +type SnippetWriter struct { + w io.Writer + context *Context + // Left & right delimiters. text/template defaults to "{{" and "}}" + // which is totally unusable for go code based templates. + left, right string + funcMap template.FuncMap + err error +} + +// w is the destination; left and right are the delimiters; @ and $ are both +// reasonable choices. +// +// c is used to make a function for every naming system, to which you can pass +// a type and get the corresponding name. +func NewSnippetWriter(w io.Writer, c *Context, left, right string) *SnippetWriter { + sw := &SnippetWriter{ + w: w, + context: c, + left: left, + right: right, + funcMap: template.FuncMap{}, + } + for name, namer := range c.Namers { + sw.funcMap[name] = namer.Name + } + return sw +} + +// Do parses format and runs args through it. You can have arbitrary logic in +// the format (see the text/template documentation), but consider running many +// short templaces, with ordinary go logic in between--this may be more +// readable. Do is chainable. Any error causes every other call to do to be +// ignored, and the error will be returned by Error(). So you can check it just +// once, at the end of your function. +// +// 'args' can be quite literally anything; read the text/template documentation +// for details. Maps and structs work particularly nicely. Conveniently, the +// types package is designed to have structs that are easily referencable from +// the template language. +// +// Example: +// +// sw := generator.NewSnippetWriter(outBuffer, context, "$", "$") +// sw.Do(`The public type name is: $.type|public$`, map[string]interface{}{"type": t}) +// return sw.Error() +// +// Where: +// * "$" starts a template directive +// * "." references the entire thing passed as args +// * "type" therefore sees a map and looks up the key "type" +// * "|" means "pass the thing on the left to the thing on the right" +// * "public" is the name of a naming system, so the SnippetWriter has given +// the template a function called "public" that takes a *types.Type and +// returns the naming system's name. E.g., if the type is "string" this might +// return "String". +// * the second "$" ends the template directive. +// +// The map is actually not necessary. The below does the same thing: +// +// sw.Do(`The public type name is: $.|public$`, t) +// +// You may or may not find it more readable to use the map with a descriptive +// key, but if you want to pass more than one arg, the map or a custom struct +// becomes a requirement. You can do arbitrary logic inside these templates, +// but you should consider doing the logic in go and stitching them together +// for the sake of your readers. +// +// TODO: Change Do() to optionally take a list of pairs of parameters (key, value) +// and have it construct a combined map with that and args. +func (s *SnippetWriter) Do(format string, args interface{}) *SnippetWriter { + if s.err != nil { + return s + } + // Name the template by source file:line so it can be found when + // there's an error. + _, file, line, _ := runtime.Caller(1) + tmpl, err := template. + New(fmt.Sprintf("%s:%d", file, line)). + Delims(s.left, s.right). + Funcs(s.funcMap). + Parse(format) + if err != nil { + s.err = err + return s + } + err = tmpl.Execute(s.w, args) + if err != nil { + s.err = err + } + return s +} + +// Args exists to make it convenient to construct arguments for +// SnippetWriter.Do. +type Args map[interface{}]interface{} + +// With makes a copy of a and adds the given key, value pair. +func (a Args) With(key, value interface{}) Args { + a2 := Args{key: value} + for k, v := range a { + a2[k] = v + } + return a2 +} + +// WithArgs makes a copy of a and adds the given arguments. +func (a Args) WithArgs(rhs Args) Args { + a2 := Args{} + for k, v := range rhs { + a2[k] = v + } + for k, v := range a { + a2[k] = v + } + return a2 +} + +func (s *SnippetWriter) Out() io.Writer { + return s.w +} + +// Error returns any encountered error. +func (s *SnippetWriter) Error() error { + return s.err +} diff --git a/vendor/k8s.io/gengo/namer/doc.go b/vendor/k8s.io/gengo/namer/doc.go new file mode 100644 index 0000000000..8a44ea9959 --- /dev/null +++ b/vendor/k8s.io/gengo/namer/doc.go @@ -0,0 +1,31 @@ +/* +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 namer has support for making different type naming systems. +// +// This is because sometimes you want to refer to the literal type, sometimes +// you want to make a name for the thing you're generating, and you want to +// make the name based on the type. For example, if you have `type foo string`, +// you want to be able to generate something like `func FooPrinter(f *foo) { +// Print(string(*f)) }`; that is, you want to refer to a public name, a literal +// name, and the underlying literal name. +// +// This package supports the idea of a "Namer" and a set of "NameSystems" to +// support these use cases. +// +// Additionally, a "RawNamer" can optionally keep track of what needs to be +// imported. +package namer // import "k8s.io/gengo/namer" diff --git a/vendor/k8s.io/gengo/namer/import_tracker.go b/vendor/k8s.io/gengo/namer/import_tracker.go new file mode 100644 index 0000000000..37094b2deb --- /dev/null +++ b/vendor/k8s.io/gengo/namer/import_tracker.go @@ -0,0 +1,112 @@ +/* +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 namer + +import ( + "sort" + + "k8s.io/gengo/types" +) + +// ImportTracker may be passed to a namer.RawNamer, to track the imports needed +// for the types it names. +// +// TODO: pay attention to the package name (instead of renaming every package). +type DefaultImportTracker struct { + pathToName map[string]string + // forbidden names are in here. (e.g. "go" is a directory in which + // there is code, but "go" is not a legal name for a package, so we put + // it here to prevent us from naming any package "go") + nameToPath map[string]string + local types.Name + + // Returns true if a given types is an invalid type and should be ignored. + IsInvalidType func(*types.Type) bool + // Returns the final local name for the given name + LocalName func(types.Name) string + // Returns the "import" line for a given (path, name). + PrintImport func(string, string) string +} + +func NewDefaultImportTracker(local types.Name) DefaultImportTracker { + return DefaultImportTracker{ + pathToName: map[string]string{}, + nameToPath: map[string]string{}, + local: local, + } +} + +func (tracker *DefaultImportTracker) AddTypes(types ...*types.Type) { + for _, t := range types { + tracker.AddType(t) + } +} +func (tracker *DefaultImportTracker) AddType(t *types.Type) { + if tracker.local.Package == t.Name.Package { + return + } + + if tracker.IsInvalidType(t) { + if t.Kind == types.Builtin { + return + } + if _, ok := tracker.nameToPath[t.Name.Package]; !ok { + tracker.nameToPath[t.Name.Package] = "" + } + return + } + + if len(t.Name.Package) == 0 { + return + } + path := t.Name.Path + if len(path) == 0 { + path = t.Name.Package + } + if _, ok := tracker.pathToName[path]; ok { + return + } + name := tracker.LocalName(t.Name) + tracker.nameToPath[name] = path + tracker.pathToName[path] = name +} + +func (tracker *DefaultImportTracker) ImportLines() []string { + importPaths := []string{} + for path := range tracker.pathToName { + importPaths = append(importPaths, path) + } + sort.Sort(sort.StringSlice(importPaths)) + out := []string{} + for _, path := range importPaths { + out = append(out, tracker.PrintImport(path, tracker.pathToName[path])) + } + return out +} + +// LocalNameOf returns the name you would use to refer to the package at the +// specified path within the body of a file. +func (tracker *DefaultImportTracker) LocalNameOf(path string) string { + return tracker.pathToName[path] +} + +// PathOf returns the path that a given localName is referring to within the +// body of a file. +func (tracker *DefaultImportTracker) PathOf(localName string) (string, bool) { + name, ok := tracker.nameToPath[localName] + return name, ok +} diff --git a/vendor/k8s.io/gengo/namer/namer.go b/vendor/k8s.io/gengo/namer/namer.go new file mode 100644 index 0000000000..d700a00a53 --- /dev/null +++ b/vendor/k8s.io/gengo/namer/namer.go @@ -0,0 +1,383 @@ +/* +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 namer + +import ( + "path/filepath" + "strings" + + "k8s.io/gengo/types" +) + +const ( + // GoSeperator is used to split go import paths. + // Forward slash is used instead of filepath.Seperator because it is the + // only universally-accepted path delimiter and the only delimiter not + // potentially forbidden by Go compilers. (In particular gc does not allow + // the use of backslashes in import paths.) + // See https://golang.org/ref/spec#Import_declarations. + // See also https://github.com/kubernetes/gengo/issues/83#issuecomment-367040772. + GoSeperator = "/" +) + +// Returns whether a name is a private Go name. +func IsPrivateGoName(name string) bool { + return len(name) == 0 || strings.ToLower(name[:1]) == name[:1] +} + +// NewPublicNamer is a helper function that returns a namer that makes +// CamelCase names. See the NameStrategy struct for an explanation of the +// arguments to this constructor. +func NewPublicNamer(prependPackageNames int, ignoreWords ...string) *NameStrategy { + n := &NameStrategy{ + Join: Joiner(IC, IC), + IgnoreWords: map[string]bool{}, + PrependPackageNames: prependPackageNames, + } + for _, w := range ignoreWords { + n.IgnoreWords[w] = true + } + return n +} + +// NewPrivateNamer is a helper function that returns a namer that makes +// camelCase names. See the NameStrategy struct for an explanation of the +// arguments to this constructor. +func NewPrivateNamer(prependPackageNames int, ignoreWords ...string) *NameStrategy { + n := &NameStrategy{ + Join: Joiner(IL, IC), + IgnoreWords: map[string]bool{}, + PrependPackageNames: prependPackageNames, + } + for _, w := range ignoreWords { + n.IgnoreWords[w] = true + } + return n +} + +// NewRawNamer will return a Namer that makes a name by which you would +// directly refer to a type, optionally keeping track of the import paths +// necessary to reference the names it provides. Tracker may be nil. +// The 'pkg' is the full package name, in which the Namer is used - all +// types from that package will be referenced by just type name without +// referencing the package. +// +// For example, if the type is map[string]int, a raw namer will literally +// return "map[string]int". +// +// Or if the type, in package foo, is "type Bar struct { ... }", then the raw +// namer will return "foo.Bar" as the name of the type, and if 'tracker' was +// not nil, will record that package foo needs to be imported. +func NewRawNamer(pkg string, tracker ImportTracker) *rawNamer { + return &rawNamer{pkg: pkg, tracker: tracker} +} + +// Names is a map from Type to name, as defined by some Namer. +type Names map[*types.Type]string + +// Namer takes a type, and assigns a name. +// +// The purpose of this complexity is so that you can assign coherent +// side-by-side systems of names for the types. For example, you might want a +// public interface, a private implementation struct, and also to reference +// literally the type name. +// +// Note that it is safe to call your own Name() function recursively to find +// the names of keys, elements, etc. This is because anonymous types can't have +// cycles in their names, and named types don't require the sort of recursion +// that would be problematic. +type Namer interface { + Name(*types.Type) string +} + +// NameSystems is a map of a system name to a namer for that system. +type NameSystems map[string]Namer + +// NameStrategy is a general Namer. The easiest way to use it is to copy the +// Public/PrivateNamer variables, and modify the members you wish to change. +// +// The Name method produces a name for the given type, of the forms: +// Anonymous types: +// Named types: +// +// In all cases, every part of the name is run through the capitalization +// functions. +// +// The IgnoreWords map can be set if you have directory names that are +// semantically meaningless for naming purposes, e.g. "proto". +// +// Prefix and Suffix can be used to disambiguate parallel systems of type +// names. For example, if you want to generate an interface and an +// implementation, you might want to suffix one with "Interface" and the other +// with "Implementation". Another common use-- if you want to generate private +// types, and one of your source types could be "string", you can't use the +// default lowercase private namer. You'll have to add a suffix or prefix. +type NameStrategy struct { + Prefix, Suffix string + Join func(pre string, parts []string, post string) string + + // Add non-meaningful package directory names here (e.g. "proto") and + // they will be ignored. + IgnoreWords map[string]bool + + // If > 0, prepend exactly that many package directory names (or as + // many as there are). Package names listed in "IgnoreWords" will be + // ignored. + // + // For example, if Ignore words lists "proto" and type Foo is in + // pkg/server/frobbing/proto, then a value of 1 will give a type name + // of FrobbingFoo, 2 gives ServerFrobbingFoo, etc. + PrependPackageNames int + + // A cache of names thus far assigned by this namer. + Names +} + +// IC ensures the first character is uppercase. +func IC(in string) string { + if in == "" { + return in + } + return strings.ToUpper(in[:1]) + in[1:] +} + +// IL ensures the first character is lowercase. +func IL(in string) string { + if in == "" { + return in + } + return strings.ToLower(in[:1]) + in[1:] +} + +// Joiner lets you specify functions that preprocess the various components of +// a name before joining them. You can construct e.g. camelCase or CamelCase or +// any other way of joining words. (See the IC and IL convenience functions.) +func Joiner(first, others func(string) string) func(pre string, in []string, post string) string { + return func(pre string, in []string, post string) string { + tmp := []string{others(pre)} + for i := range in { + tmp = append(tmp, others(in[i])) + } + tmp = append(tmp, others(post)) + return first(strings.Join(tmp, "")) + } +} + +func (ns *NameStrategy) removePrefixAndSuffix(s string) string { + // The join function may have changed capitalization. + lowerIn := strings.ToLower(s) + lowerP := strings.ToLower(ns.Prefix) + lowerS := strings.ToLower(ns.Suffix) + b, e := 0, len(s) + if strings.HasPrefix(lowerIn, lowerP) { + b = len(ns.Prefix) + } + if strings.HasSuffix(lowerIn, lowerS) { + e -= len(ns.Suffix) + } + return s[b:e] +} + +var ( + importPathNameSanitizer = strings.NewReplacer("-", "_", ".", "") +) + +// filters out unwanted directory names and sanitizes remaining names. +func (ns *NameStrategy) filterDirs(path string) []string { + allDirs := strings.Split(path, GoSeperator) + dirs := make([]string, 0, len(allDirs)) + for _, p := range allDirs { + if ns.IgnoreWords == nil || !ns.IgnoreWords[p] { + dirs = append(dirs, importPathNameSanitizer.Replace(p)) + } + } + return dirs +} + +// See the comment on NameStrategy. +func (ns *NameStrategy) Name(t *types.Type) string { + if ns.Names == nil { + ns.Names = Names{} + } + if s, ok := ns.Names[t]; ok { + return s + } + + if t.Name.Package != "" { + dirs := append(ns.filterDirs(t.Name.Package), t.Name.Name) + i := ns.PrependPackageNames + 1 + dn := len(dirs) + if i > dn { + i = dn + } + name := ns.Join(ns.Prefix, dirs[dn-i:], ns.Suffix) + ns.Names[t] = name + return name + } + + // Only anonymous types remain. + var name string + switch t.Kind { + case types.Builtin: + name = ns.Join(ns.Prefix, []string{t.Name.Name}, ns.Suffix) + case types.Map: + name = ns.Join(ns.Prefix, []string{ + "Map", + ns.removePrefixAndSuffix(ns.Name(t.Key)), + "To", + ns.removePrefixAndSuffix(ns.Name(t.Elem)), + }, ns.Suffix) + case types.Slice: + name = ns.Join(ns.Prefix, []string{ + "Slice", + ns.removePrefixAndSuffix(ns.Name(t.Elem)), + }, ns.Suffix) + case types.Pointer: + name = ns.Join(ns.Prefix, []string{ + "Pointer", + ns.removePrefixAndSuffix(ns.Name(t.Elem)), + }, ns.Suffix) + case types.Struct: + names := []string{"Struct"} + for _, m := range t.Members { + names = append(names, ns.removePrefixAndSuffix(ns.Name(m.Type))) + } + name = ns.Join(ns.Prefix, names, ns.Suffix) + case types.Chan: + name = ns.Join(ns.Prefix, []string{ + "Chan", + ns.removePrefixAndSuffix(ns.Name(t.Elem)), + }, ns.Suffix) + case types.Interface: + // TODO: add to name test + names := []string{"Interface"} + for _, m := range t.Methods { + // TODO: include function signature + names = append(names, m.Name.Name) + } + name = ns.Join(ns.Prefix, names, ns.Suffix) + case types.Func: + // TODO: add to name test + parts := []string{"Func"} + for _, pt := range t.Signature.Parameters { + parts = append(parts, ns.removePrefixAndSuffix(ns.Name(pt))) + } + parts = append(parts, "Returns") + for _, rt := range t.Signature.Results { + parts = append(parts, ns.removePrefixAndSuffix(ns.Name(rt))) + } + name = ns.Join(ns.Prefix, parts, ns.Suffix) + default: + name = "unnameable_" + string(t.Kind) + } + ns.Names[t] = name + return name +} + +// ImportTracker allows a raw namer to keep track of the packages needed for +// import. You can implement yourself or use the one in the generation package. +type ImportTracker interface { + AddType(*types.Type) + LocalNameOf(packagePath string) string + PathOf(localName string) (string, bool) + ImportLines() []string +} + +type rawNamer struct { + pkg string + tracker ImportTracker + Names +} + +// Name makes a name the way you'd write it to literally refer to type t, +// making ordinary assumptions about how you've imported t's package (or using +// r.tracker to specifically track the package imports). +func (r *rawNamer) Name(t *types.Type) string { + if r.Names == nil { + r.Names = Names{} + } + if name, ok := r.Names[t]; ok { + return name + } + if t.Name.Package != "" { + var name string + if r.tracker != nil { + r.tracker.AddType(t) + if t.Name.Package == r.pkg { + name = t.Name.Name + } else { + name = r.tracker.LocalNameOf(t.Name.Package) + "." + t.Name.Name + } + } else { + if t.Name.Package == r.pkg { + name = t.Name.Name + } else { + name = filepath.Base(t.Name.Package) + "." + t.Name.Name + } + } + r.Names[t] = name + return name + } + var name string + switch t.Kind { + case types.Builtin: + name = t.Name.Name + case types.Map: + name = "map[" + r.Name(t.Key) + "]" + r.Name(t.Elem) + case types.Slice: + name = "[]" + r.Name(t.Elem) + case types.Pointer: + name = "*" + r.Name(t.Elem) + case types.Struct: + elems := []string{} + for _, m := range t.Members { + elems = append(elems, m.Name+" "+r.Name(m.Type)) + } + name = "struct{" + strings.Join(elems, "; ") + "}" + case types.Chan: + // TODO: include directionality + name = "chan " + r.Name(t.Elem) + case types.Interface: + // TODO: add to name test + elems := []string{} + for _, m := range t.Methods { + // TODO: include function signature + elems = append(elems, m.Name.Name) + } + name = "interface{" + strings.Join(elems, "; ") + "}" + case types.Func: + // TODO: add to name test + params := []string{} + for _, pt := range t.Signature.Parameters { + params = append(params, r.Name(pt)) + } + results := []string{} + for _, rt := range t.Signature.Results { + results = append(results, r.Name(rt)) + } + name = "func(" + strings.Join(params, ",") + ")" + if len(results) == 1 { + name += " " + results[0] + } else if len(results) > 1 { + name += " (" + strings.Join(results, ",") + ")" + } + default: + name = "unnameable_" + string(t.Kind) + } + r.Names[t] = name + return name +} diff --git a/vendor/k8s.io/gengo/namer/order.go b/vendor/k8s.io/gengo/namer/order.go new file mode 100644 index 0000000000..f86282b9b1 --- /dev/null +++ b/vendor/k8s.io/gengo/namer/order.go @@ -0,0 +1,69 @@ +/* +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 namer + +import ( + "sort" + + "k8s.io/gengo/types" +) + +// Orderer produces an ordering of types given a Namer. +type Orderer struct { + Namer +} + +// OrderUniverse assigns a name to every type in the Universe, including Types, +// Functions and Variables, and returns a list sorted by those names. +func (o *Orderer) OrderUniverse(u types.Universe) []*types.Type { + list := tList{ + namer: o.Namer, + } + for _, p := range u { + for _, t := range p.Types { + list.types = append(list.types, t) + } + for _, f := range p.Functions { + list.types = append(list.types, f) + } + for _, v := range p.Variables { + list.types = append(list.types, v) + } + } + sort.Sort(list) + return list.types +} + +// OrderTypes assigns a name to every type, and returns a list sorted by those +// names. +func (o *Orderer) OrderTypes(typeList []*types.Type) []*types.Type { + list := tList{ + namer: o.Namer, + types: typeList, + } + sort.Sort(list) + return list.types +} + +type tList struct { + namer Namer + types []*types.Type +} + +func (t tList) Len() int { return len(t.types) } +func (t tList) Less(i, j int) bool { return t.namer.Name(t.types[i]) < t.namer.Name(t.types[j]) } +func (t tList) Swap(i, j int) { t.types[i], t.types[j] = t.types[j], t.types[i] } diff --git a/vendor/k8s.io/gengo/namer/plural_namer.go b/vendor/k8s.io/gengo/namer/plural_namer.go new file mode 100644 index 0000000000..a9a198a702 --- /dev/null +++ b/vendor/k8s.io/gengo/namer/plural_namer.go @@ -0,0 +1,120 @@ +/* +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 namer + +import ( + "strings" + + "k8s.io/gengo/types" +) + +var consonants = "bcdfghjklmnpqrsttvwxyz" + +type pluralNamer struct { + // key is the case-sensitive type name, value is the case-insensitive + // intended output. + exceptions map[string]string + finalize func(string) string +} + +// NewPublicPluralNamer returns a namer that returns the plural form of the input +// type's name, starting with a uppercase letter. +func NewPublicPluralNamer(exceptions map[string]string) *pluralNamer { + return &pluralNamer{exceptions, IC} +} + +// NewPrivatePluralNamer returns a namer that returns the plural form of the input +// type's name, starting with a lowercase letter. +func NewPrivatePluralNamer(exceptions map[string]string) *pluralNamer { + return &pluralNamer{exceptions, IL} +} + +// NewAllLowercasePluralNamer returns a namer that returns the plural form of the input +// type's name, with all letters in lowercase. +func NewAllLowercasePluralNamer(exceptions map[string]string) *pluralNamer { + return &pluralNamer{exceptions, strings.ToLower} +} + +// Name returns the plural form of the type's name. If the type's name is found +// in the exceptions map, the map value is returned. +func (r *pluralNamer) Name(t *types.Type) string { + singular := t.Name.Name + var plural string + var ok bool + if plural, ok = r.exceptions[singular]; ok { + return r.finalize(plural) + } + if len(singular) < 2 { + return r.finalize(singular) + } + + switch rune(singular[len(singular)-1]) { + case 's', 'x', 'z': + plural = esPlural(singular) + case 'y': + sl := rune(singular[len(singular)-2]) + if isConsonant(sl) { + plural = iesPlural(singular) + } else { + plural = sPlural(singular) + } + case 'h': + sl := rune(singular[len(singular)-2]) + if sl == 'c' || sl == 's' { + plural = esPlural(singular) + } else { + plural = sPlural(singular) + } + case 'e': + sl := rune(singular[len(singular)-2]) + if sl == 'f' { + plural = vesPlural(singular[:len(singular)-1]) + } else { + plural = sPlural(singular) + } + case 'f': + plural = vesPlural(singular) + default: + plural = sPlural(singular) + } + return r.finalize(plural) +} + +func iesPlural(singular string) string { + return singular[:len(singular)-1] + "ies" +} + +func vesPlural(singular string) string { + return singular[:len(singular)-1] + "ves" +} + +func esPlural(singular string) string { + return singular + "es" +} + +func sPlural(singular string) string { + return singular + "s" +} + +func isConsonant(char rune) bool { + for _, c := range consonants { + if char == c { + return true + } + } + return false +} diff --git a/vendor/k8s.io/gengo/parser/doc.go b/vendor/k8s.io/gengo/parser/doc.go new file mode 100644 index 0000000000..8231b6d432 --- /dev/null +++ b/vendor/k8s.io/gengo/parser/doc.go @@ -0,0 +1,19 @@ +/* +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 parser provides code to parse go files, type-check them, extract the +// types. +package parser // import "k8s.io/gengo/parser" diff --git a/vendor/k8s.io/gengo/parser/parse.go b/vendor/k8s.io/gengo/parser/parse.go new file mode 100644 index 0000000000..1c6c55019c --- /dev/null +++ b/vendor/k8s.io/gengo/parser/parse.go @@ -0,0 +1,813 @@ +/* +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 parser + +import ( + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/token" + tc "go/types" + "io/ioutil" + "os" + "os/exec" + "path" + "path/filepath" + "sort" + "strings" + + "k8s.io/gengo/types" + "k8s.io/klog" +) + +// This clarifies when a pkg path has been canonicalized. +type importPathString string + +// Builder lets you add all the go files in all the packages that you care +// about, then constructs the type source data. +type Builder struct { + context *build.Context + + // Map of package names to more canonical information about the package. + // This might hold the same value for multiple names, e.g. if someone + // referenced ./pkg/name or in the case of vendoring, which canonicalizes + // differently that what humans would type. + buildPackages map[string]*build.Package + + fset *token.FileSet + // map of package path to list of parsed files + parsed map[importPathString][]parsedFile + // map of package path to absolute path (to prevent overlap) + absPaths map[importPathString]string + + // Set by typeCheckPackage(), used by importPackage() and friends. + typeCheckedPackages map[importPathString]*tc.Package + + // Map of package path to whether the user requested it or it was from + // an import. + userRequested map[importPathString]bool + + // All comments from everywhere in every parsed file. + endLineToCommentGroup map[fileLine]*ast.CommentGroup + + // map of package to list of packages it imports. + importGraph map[importPathString]map[string]struct{} +} + +// parsedFile is for tracking files with name +type parsedFile struct { + name string + file *ast.File +} + +// key type for finding comments. +type fileLine struct { + file string + line int +} + +// New constructs a new builder. +func New() *Builder { + c := build.Default + if c.GOROOT == "" { + if p, err := exec.Command("which", "go").CombinedOutput(); err == nil { + // The returned string will have some/path/bin/go, so remove the last two elements. + c.GOROOT = filepath.Dir(filepath.Dir(strings.Trim(string(p), "\n"))) + } else { + klog.Warningf("Warning: $GOROOT not set, and unable to run `which go` to find it: %v\n", err) + } + } + // Force this to off, since we don't properly parse CGo. All symbols must + // have non-CGo equivalents. + c.CgoEnabled = false + return &Builder{ + context: &c, + buildPackages: map[string]*build.Package{}, + typeCheckedPackages: map[importPathString]*tc.Package{}, + fset: token.NewFileSet(), + parsed: map[importPathString][]parsedFile{}, + absPaths: map[importPathString]string{}, + userRequested: map[importPathString]bool{}, + endLineToCommentGroup: map[fileLine]*ast.CommentGroup{}, + importGraph: map[importPathString]map[string]struct{}{}, + } +} + +// AddBuildTags adds the specified build tags to the parse context. +func (b *Builder) AddBuildTags(tags ...string) { + b.context.BuildTags = append(b.context.BuildTags, tags...) +} + +// Get package information from the go/build package. Automatically excludes +// e.g. test files and files for other platforms-- there is quite a bit of +// logic of that nature in the build package. +func (b *Builder) importBuildPackage(dir string) (*build.Package, error) { + if buildPkg, ok := b.buildPackages[dir]; ok { + return buildPkg, nil + } + // This validates the `package foo // github.com/bar/foo` comments. + buildPkg, err := b.importWithMode(dir, build.ImportComment) + if err != nil { + if _, ok := err.(*build.NoGoError); !ok { + return nil, fmt.Errorf("unable to import %q: %v", dir, err) + } + } + if buildPkg == nil { + // Might be an empty directory. Try to just find the dir. + buildPkg, err = b.importWithMode(dir, build.FindOnly) + if err != nil { + return nil, err + } + } + + // Remember it under the user-provided name. + klog.V(5).Infof("saving buildPackage %s", dir) + b.buildPackages[dir] = buildPkg + canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath) + if dir != string(canonicalPackage) { + // Since `dir` is not the canonical name, see if we knew it under another name. + if buildPkg, ok := b.buildPackages[string(canonicalPackage)]; ok { + return buildPkg, nil + } + // Must be new, save it under the canonical name, too. + klog.V(5).Infof("saving buildPackage %s", canonicalPackage) + b.buildPackages[string(canonicalPackage)] = buildPkg + } + + return buildPkg, nil +} + +// AddFileForTest adds a file to the set, without verifying that the provided +// pkg actually exists on disk. The pkg must be of the form "canonical/pkg/path" +// and the path must be the absolute path to the file. Because this bypasses +// the normal recursive finding of package dependencies (on disk), test should +// sort their test files topologically first, so all deps are resolved by the +// time we need them. +func (b *Builder) AddFileForTest(pkg string, path string, src []byte) error { + if err := b.addFile(importPathString(pkg), path, src, true); err != nil { + return err + } + if _, err := b.typeCheckPackage(importPathString(pkg)); err != nil { + return err + } + return nil +} + +// addFile adds a file to the set. The pkgPath must be of the form +// "canonical/pkg/path" and the path must be the absolute path to the file. A +// flag indicates whether this file was user-requested or just from following +// the import graph. +func (b *Builder) addFile(pkgPath importPathString, path string, src []byte, userRequested bool) error { + for _, p := range b.parsed[pkgPath] { + if path == p.name { + klog.V(5).Infof("addFile %s %s already parsed, skipping", pkgPath, path) + return nil + } + } + klog.V(6).Infof("addFile %s %s", pkgPath, path) + p, err := parser.ParseFile(b.fset, path, src, parser.DeclarationErrors|parser.ParseComments) + if err != nil { + return err + } + + // This is redundant with addDir, but some tests call AddFileForTest, which + // call into here without calling addDir. + b.userRequested[pkgPath] = userRequested || b.userRequested[pkgPath] + + b.parsed[pkgPath] = append(b.parsed[pkgPath], parsedFile{path, p}) + for _, c := range p.Comments { + position := b.fset.Position(c.End()) + b.endLineToCommentGroup[fileLine{position.Filename, position.Line}] = c + } + + // We have to get the packages from this specific file, in case the + // user added individual files instead of entire directories. + if b.importGraph[pkgPath] == nil { + b.importGraph[pkgPath] = map[string]struct{}{} + } + for _, im := range p.Imports { + importedPath := strings.Trim(im.Path.Value, `"`) + b.importGraph[pkgPath][importedPath] = struct{}{} + } + return nil +} + +// AddDir adds an entire directory, scanning it for go files. 'dir' should have +// a single go package in it. GOPATH, GOROOT, and the location of your go +// binary (`which go`) will all be searched if dir doesn't literally resolve. +func (b *Builder) AddDir(dir string) error { + _, err := b.importPackage(dir, true) + return err +} + +// AddDirRecursive is just like AddDir, but it also recursively adds +// subdirectories; it returns an error only if the path couldn't be resolved; +// any directories recursed into without go source are ignored. +func (b *Builder) AddDirRecursive(dir string) error { + // Add the root. + if _, err := b.importPackage(dir, true); err != nil { + klog.Warningf("Ignoring directory %v: %v", dir, err) + } + + // filepath.Walk includes the root dir, but we already did that, so we'll + // remove that prefix and rebuild a package import path. + prefix := b.buildPackages[dir].Dir + fn := func(filePath string, info os.FileInfo, err error) error { + if info != nil && info.IsDir() { + rel := filepath.ToSlash(strings.TrimPrefix(filePath, prefix)) + if rel != "" { + // Make a pkg path. + pkg := path.Join(string(canonicalizeImportPath(b.buildPackages[dir].ImportPath)), rel) + + // Add it. + if _, err := b.importPackage(pkg, true); err != nil { + klog.Warningf("Ignoring child directory %v: %v", pkg, err) + } + } + } + return nil + } + if err := filepath.Walk(b.buildPackages[dir].Dir, fn); err != nil { + return err + } + return nil +} + +// AddDirTo adds an entire directory to a given Universe. Unlike AddDir, this +// processes the package immediately, which makes it safe to use from within a +// generator (rather than just at init time. 'dir' must be a single go package. +// GOPATH, GOROOT, and the location of your go binary (`which go`) will all be +// searched if dir doesn't literally resolve. +// Deprecated. Please use AddDirectoryTo. +func (b *Builder) AddDirTo(dir string, u *types.Universe) error { + // We want all types from this package, as if they were directly added + // by the user. They WERE added by the user, in effect. + if _, err := b.importPackage(dir, true); err != nil { + return err + } + return b.findTypesIn(canonicalizeImportPath(b.buildPackages[dir].ImportPath), u) +} + +// AddDirectoryTo adds an entire directory to a given Universe. Unlike AddDir, +// this processes the package immediately, which makes it safe to use from +// within a generator (rather than just at init time. 'dir' must be a single go +// package. GOPATH, GOROOT, and the location of your go binary (`which go`) +// will all be searched if dir doesn't literally resolve. +func (b *Builder) AddDirectoryTo(dir string, u *types.Universe) (*types.Package, error) { + // We want all types from this package, as if they were directly added + // by the user. They WERE added by the user, in effect. + if _, err := b.importPackage(dir, true); err != nil { + return nil, err + } + path := canonicalizeImportPath(b.buildPackages[dir].ImportPath) + if err := b.findTypesIn(path, u); err != nil { + return nil, err + } + return u.Package(string(path)), nil +} + +// The implementation of AddDir. A flag indicates whether this directory was +// user-requested or just from following the import graph. +func (b *Builder) addDir(dir string, userRequested bool) error { + klog.V(5).Infof("addDir %s", dir) + buildPkg, err := b.importBuildPackage(dir) + if err != nil { + return err + } + canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath) + pkgPath := canonicalPackage + if dir != string(canonicalPackage) { + klog.V(5).Infof("addDir %s, canonical path is %s", dir, pkgPath) + } + + // Sanity check the pkg dir has not changed. + if prev, found := b.absPaths[pkgPath]; found { + if buildPkg.Dir != prev { + return fmt.Errorf("package %q (%s) previously resolved to %s", pkgPath, buildPkg.Dir, prev) + } + } else { + b.absPaths[pkgPath] = buildPkg.Dir + } + + for _, n := range buildPkg.GoFiles { + if !strings.HasSuffix(n, ".go") { + continue + } + absPath := filepath.Join(buildPkg.Dir, n) + data, err := ioutil.ReadFile(absPath) + if err != nil { + return fmt.Errorf("while loading %q: %v", absPath, err) + } + err = b.addFile(pkgPath, absPath, data, userRequested) + if err != nil { + return fmt.Errorf("while parsing %q: %v", absPath, err) + } + } + return nil +} + +// importPackage is a function that will be called by the type check package when it +// needs to import a go package. 'path' is the import path. +func (b *Builder) importPackage(dir string, userRequested bool) (*tc.Package, error) { + klog.V(5).Infof("importPackage %s", dir) + var pkgPath = importPathString(dir) + + // Get the canonical path if we can. + if buildPkg := b.buildPackages[dir]; buildPkg != nil { + canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath) + klog.V(5).Infof("importPackage %s, canonical path is %s", dir, canonicalPackage) + pkgPath = canonicalPackage + } + + // If we have not seen this before, process it now. + ignoreError := false + if _, found := b.parsed[pkgPath]; !found { + // Ignore errors in paths that we're importing solely because + // they're referenced by other packages. + ignoreError = true + + // Add it. + if err := b.addDir(dir, userRequested); err != nil { + return nil, err + } + + // Get the canonical path now that it has been added. + if buildPkg := b.buildPackages[dir]; buildPkg != nil { + canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath) + klog.V(5).Infof("importPackage %s, canonical path is %s", dir, canonicalPackage) + pkgPath = canonicalPackage + } + } + + // If it was previously known, just check that the user-requestedness hasn't + // changed. + b.userRequested[pkgPath] = userRequested || b.userRequested[pkgPath] + + // Run the type checker. We may end up doing this to pkgs that are already + // done, or are in the queue to be done later, but it will short-circuit, + // and we can't miss pkgs that are only depended on. + pkg, err := b.typeCheckPackage(pkgPath) + if err != nil { + switch { + case ignoreError && pkg != nil: + klog.V(2).Infof("type checking encountered some issues in %q, but ignoring.\n", pkgPath) + case !ignoreError && pkg != nil: + klog.V(2).Infof("type checking encountered some errors in %q\n", pkgPath) + return nil, err + default: + return nil, err + } + } + + return pkg, nil +} + +type importAdapter struct { + b *Builder +} + +func (a importAdapter) Import(path string) (*tc.Package, error) { + return a.b.importPackage(path, false) +} + +// typeCheckPackage will attempt to return the package even if there are some +// errors, so you may check whether the package is nil or not even if you get +// an error. +func (b *Builder) typeCheckPackage(pkgPath importPathString) (*tc.Package, error) { + klog.V(5).Infof("typeCheckPackage %s", pkgPath) + if pkg, ok := b.typeCheckedPackages[pkgPath]; ok { + if pkg != nil { + klog.V(6).Infof("typeCheckPackage %s already done", pkgPath) + return pkg, nil + } + // We store a nil right before starting work on a package. So + // if we get here and it's present and nil, that means there's + // another invocation of this function on the call stack + // already processing this package. + return nil, fmt.Errorf("circular dependency for %q", pkgPath) + } + parsedFiles, ok := b.parsed[pkgPath] + if !ok { + return nil, fmt.Errorf("No files for pkg %q", pkgPath) + } + files := make([]*ast.File, len(parsedFiles)) + for i := range parsedFiles { + files[i] = parsedFiles[i].file + } + b.typeCheckedPackages[pkgPath] = nil + c := tc.Config{ + IgnoreFuncBodies: true, + // Note that importAdapter can call b.importPackage which calls this + // method. So there can't be cycles in the import graph. + Importer: importAdapter{b}, + Error: func(err error) { + klog.V(2).Infof("type checker: %v\n", err) + }, + } + pkg, err := c.Check(string(pkgPath), b.fset, files, nil) + b.typeCheckedPackages[pkgPath] = pkg // record the result whether or not there was an error + return pkg, err +} + +// FindPackages fetches a list of the user-imported packages. +// Note that you need to call b.FindTypes() first. +func (b *Builder) FindPackages() []string { + // Iterate packages in a predictable order. + pkgPaths := []string{} + for k := range b.typeCheckedPackages { + pkgPaths = append(pkgPaths, string(k)) + } + sort.Strings(pkgPaths) + + result := []string{} + for _, pkgPath := range pkgPaths { + if b.userRequested[importPathString(pkgPath)] { + // Since walkType is recursive, all types that are in packages that + // were directly mentioned will be included. We don't need to + // include all types in all transitive packages, though. + result = append(result, pkgPath) + } + } + return result +} + +// FindTypes finalizes the package imports, and searches through all the +// packages for types. +func (b *Builder) FindTypes() (types.Universe, error) { + // Take a snapshot of pkgs to iterate, since this will recursively mutate + // b.parsed. Iterate in a predictable order. + pkgPaths := []string{} + for pkgPath := range b.parsed { + pkgPaths = append(pkgPaths, string(pkgPath)) + } + sort.Strings(pkgPaths) + + u := types.Universe{} + for _, pkgPath := range pkgPaths { + if err := b.findTypesIn(importPathString(pkgPath), &u); err != nil { + return nil, err + } + } + return u, nil +} + +// findTypesIn finalizes the package import and searches through the package +// for types. +func (b *Builder) findTypesIn(pkgPath importPathString, u *types.Universe) error { + klog.V(5).Infof("findTypesIn %s", pkgPath) + pkg := b.typeCheckedPackages[pkgPath] + if pkg == nil { + return fmt.Errorf("findTypesIn(%s): package is not known", pkgPath) + } + if !b.userRequested[pkgPath] { + // Since walkType is recursive, all types that the + // packages they asked for depend on will be included. + // But we don't need to include all types in all + // *packages* they depend on. + klog.V(5).Infof("findTypesIn %s: package is not user requested", pkgPath) + return nil + } + + // We're keeping this package. This call will create the record. + u.Package(string(pkgPath)).Name = pkg.Name() + u.Package(string(pkgPath)).Path = pkg.Path() + u.Package(string(pkgPath)).SourcePath = b.absPaths[pkgPath] + + for _, f := range b.parsed[pkgPath] { + if _, fileName := filepath.Split(f.name); fileName == "doc.go" { + tp := u.Package(string(pkgPath)) + // findTypesIn might be called multiple times. Clean up tp.Comments + // to avoid repeatedly fill same comments to it. + tp.Comments = []string{} + for i := range f.file.Comments { + tp.Comments = append(tp.Comments, splitLines(f.file.Comments[i].Text())...) + } + if f.file.Doc != nil { + tp.DocComments = splitLines(f.file.Doc.Text()) + } + } + } + + s := pkg.Scope() + for _, n := range s.Names() { + obj := s.Lookup(n) + tn, ok := obj.(*tc.TypeName) + if ok { + t := b.walkType(*u, nil, tn.Type()) + c1 := b.priorCommentLines(obj.Pos(), 1) + // c1.Text() is safe if c1 is nil + t.CommentLines = splitLines(c1.Text()) + if c1 == nil { + t.SecondClosestCommentLines = splitLines(b.priorCommentLines(obj.Pos(), 2).Text()) + } else { + t.SecondClosestCommentLines = splitLines(b.priorCommentLines(c1.List[0].Slash, 2).Text()) + } + } + tf, ok := obj.(*tc.Func) + // We only care about functions, not concrete/abstract methods. + if ok && tf.Type() != nil && tf.Type().(*tc.Signature).Recv() == nil { + t := b.addFunction(*u, nil, tf) + c1 := b.priorCommentLines(obj.Pos(), 1) + // c1.Text() is safe if c1 is nil + t.CommentLines = splitLines(c1.Text()) + if c1 == nil { + t.SecondClosestCommentLines = splitLines(b.priorCommentLines(obj.Pos(), 2).Text()) + } else { + t.SecondClosestCommentLines = splitLines(b.priorCommentLines(c1.List[0].Slash, 2).Text()) + } + } + tv, ok := obj.(*tc.Var) + if ok && !tv.IsField() { + b.addVariable(*u, nil, tv) + } + } + + importedPkgs := []string{} + for k := range b.importGraph[pkgPath] { + importedPkgs = append(importedPkgs, string(k)) + } + sort.Strings(importedPkgs) + for _, p := range importedPkgs { + u.AddImports(string(pkgPath), p) + } + return nil +} + +func (b *Builder) importWithMode(dir string, mode build.ImportMode) (*build.Package, error) { + // This is a bit of a hack. The srcDir argument to Import() should + // properly be the dir of the file which depends on the package to be + // imported, so that vendoring can work properly and local paths can + // resolve. We assume that there is only one level of vendoring, and that + // the CWD is inside the GOPATH, so this should be safe. Nobody should be + // using local (relative) paths except on the CLI, so CWD is also + // sufficient. + cwd, err := os.Getwd() + if err != nil { + return nil, fmt.Errorf("unable to get current directory: %v", err) + } + buildPkg, err := b.context.Import(dir, cwd, mode) + if err != nil { + return nil, err + } + return buildPkg, nil +} + +// if there's a comment on the line `lines` before pos, return its text, otherwise "". +func (b *Builder) priorCommentLines(pos token.Pos, lines int) *ast.CommentGroup { + position := b.fset.Position(pos) + key := fileLine{position.Filename, position.Line - lines} + return b.endLineToCommentGroup[key] +} + +func splitLines(str string) []string { + return strings.Split(strings.TrimRight(str, "\n"), "\n") +} + +func tcFuncNameToName(in string) types.Name { + name := strings.TrimPrefix(in, "func ") + nameParts := strings.Split(name, "(") + return tcNameToName(nameParts[0]) +} + +func tcVarNameToName(in string) types.Name { + nameParts := strings.Split(in, " ") + // nameParts[0] is "var". + // nameParts[2:] is the type of the variable, we ignore it for now. + return tcNameToName(nameParts[1]) +} + +func tcNameToName(in string) types.Name { + // Detect anonymous type names. (These may have '.' characters because + // embedded types may have packages, so we detect them specially.) + if strings.HasPrefix(in, "struct{") || + strings.HasPrefix(in, "<-chan") || + strings.HasPrefix(in, "chan<-") || + strings.HasPrefix(in, "chan ") || + strings.HasPrefix(in, "func(") || + strings.HasPrefix(in, "*") || + strings.HasPrefix(in, "map[") || + strings.HasPrefix(in, "[") { + return types.Name{Name: in} + } + + // Otherwise, if there are '.' characters present, the name has a + // package path in front. + nameParts := strings.Split(in, ".") + name := types.Name{Name: in} + if n := len(nameParts); n >= 2 { + // The final "." is the name of the type--previous ones must + // have been in the package path. + name.Package, name.Name = strings.Join(nameParts[:n-1], "."), nameParts[n-1] + } + return name +} + +func (b *Builder) convertSignature(u types.Universe, t *tc.Signature) *types.Signature { + signature := &types.Signature{} + for i := 0; i < t.Params().Len(); i++ { + signature.Parameters = append(signature.Parameters, b.walkType(u, nil, t.Params().At(i).Type())) + } + for i := 0; i < t.Results().Len(); i++ { + signature.Results = append(signature.Results, b.walkType(u, nil, t.Results().At(i).Type())) + } + if r := t.Recv(); r != nil { + signature.Receiver = b.walkType(u, nil, r.Type()) + } + signature.Variadic = t.Variadic() + return signature +} + +// walkType adds the type, and any necessary child types. +func (b *Builder) walkType(u types.Universe, useName *types.Name, in tc.Type) *types.Type { + // Most of the cases are underlying types of the named type. + name := tcNameToName(in.String()) + if useName != nil { + name = *useName + } + + switch t := in.(type) { + case *tc.Struct: + out := u.Type(name) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Struct + for i := 0; i < t.NumFields(); i++ { + f := t.Field(i) + m := types.Member{ + Name: f.Name(), + Embedded: f.Anonymous(), + Tags: t.Tag(i), + Type: b.walkType(u, nil, f.Type()), + CommentLines: splitLines(b.priorCommentLines(f.Pos(), 1).Text()), + } + out.Members = append(out.Members, m) + } + return out + case *tc.Map: + out := u.Type(name) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Map + out.Elem = b.walkType(u, nil, t.Elem()) + out.Key = b.walkType(u, nil, t.Key()) + return out + case *tc.Pointer: + out := u.Type(name) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Pointer + out.Elem = b.walkType(u, nil, t.Elem()) + return out + case *tc.Slice: + out := u.Type(name) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Slice + out.Elem = b.walkType(u, nil, t.Elem()) + return out + case *tc.Array: + out := u.Type(name) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Array + out.Elem = b.walkType(u, nil, t.Elem()) + // TODO: need to store array length, otherwise raw type name + // cannot be properly written. + return out + case *tc.Chan: + out := u.Type(name) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Chan + out.Elem = b.walkType(u, nil, t.Elem()) + // TODO: need to store direction, otherwise raw type name + // cannot be properly written. + return out + case *tc.Basic: + out := u.Type(types.Name{ + Package: "", + Name: t.Name(), + }) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Unsupported + return out + case *tc.Signature: + out := u.Type(name) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Func + out.Signature = b.convertSignature(u, t) + return out + case *tc.Interface: + out := u.Type(name) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Interface + t.Complete() + for i := 0; i < t.NumMethods(); i++ { + if out.Methods == nil { + out.Methods = map[string]*types.Type{} + } + out.Methods[t.Method(i).Name()] = b.walkType(u, nil, t.Method(i).Type()) + } + return out + case *tc.Named: + var out *types.Type + switch t.Underlying().(type) { + case *tc.Named, *tc.Basic, *tc.Map, *tc.Slice: + name := tcNameToName(t.String()) + out = u.Type(name) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Alias + out.Underlying = b.walkType(u, nil, t.Underlying()) + default: + // tc package makes everything "named" with an + // underlying anonymous type--we remove that annoying + // "feature" for users. This flattens those types + // together. + name := tcNameToName(t.String()) + if out := u.Type(name); out.Kind != types.Unknown { + return out // short circuit if we've already made this. + } + out = b.walkType(u, &name, t.Underlying()) + } + // If the underlying type didn't already add methods, add them. + // (Interface types will have already added methods.) + if len(out.Methods) == 0 { + for i := 0; i < t.NumMethods(); i++ { + if out.Methods == nil { + out.Methods = map[string]*types.Type{} + } + out.Methods[t.Method(i).Name()] = b.walkType(u, nil, t.Method(i).Type()) + } + } + return out + default: + out := u.Type(name) + if out.Kind != types.Unknown { + return out + } + out.Kind = types.Unsupported + klog.Warningf("Making unsupported type entry %q for: %#v\n", out, t) + return out + } +} + +func (b *Builder) addFunction(u types.Universe, useName *types.Name, in *tc.Func) *types.Type { + name := tcFuncNameToName(in.String()) + if useName != nil { + name = *useName + } + out := u.Function(name) + out.Kind = types.DeclarationOf + out.Underlying = b.walkType(u, nil, in.Type()) + return out +} + +func (b *Builder) addVariable(u types.Universe, useName *types.Name, in *tc.Var) *types.Type { + name := tcVarNameToName(in.String()) + if useName != nil { + name = *useName + } + out := u.Variable(name) + out.Kind = types.DeclarationOf + out.Underlying = b.walkType(u, nil, in.Type()) + return out +} + +// canonicalizeImportPath takes an import path and returns the actual package. +// It doesn't support nested vendoring. +func canonicalizeImportPath(importPath string) importPathString { + if !strings.Contains(importPath, "/vendor/") { + return importPathString(importPath) + } + + return importPathString(importPath[strings.Index(importPath, "/vendor/")+len("/vendor/"):]) +} diff --git a/vendor/k8s.io/gengo/types/comments.go b/vendor/k8s.io/gengo/types/comments.go new file mode 100644 index 0000000000..8150c38387 --- /dev/null +++ b/vendor/k8s.io/gengo/types/comments.go @@ -0,0 +1,82 @@ +/* +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 types contains go type information, packaged in a way that makes +// auto-generation convenient, whether by template or straight go functions. +package types + +import ( + "fmt" + "strings" +) + +// ExtractCommentTags parses comments for lines of the form: +// +// 'marker' + "key=value". +// +// Values are optional; "" is the default. A tag can be specified more than +// one time and all values are returned. If the resulting map has an entry for +// a key, the value (a slice) is guaranteed to have at least 1 element. +// +// Example: if you pass "+" for 'marker', and the following lines are in +// the comments: +// +foo=value1 +// +bar +// +foo=value2 +// +baz="qux" +// Then this function will return: +// map[string][]string{"foo":{"value1, "value2"}, "bar": {""}, "baz": {"qux"}} +func ExtractCommentTags(marker string, lines []string) map[string][]string { + out := map[string][]string{} + for _, line := range lines { + line = strings.Trim(line, " ") + if len(line) == 0 { + continue + } + if !strings.HasPrefix(line, marker) { + continue + } + // TODO: we could support multiple values per key if we split on spaces + kv := strings.SplitN(line[len(marker):], "=", 2) + if len(kv) == 2 { + out[kv[0]] = append(out[kv[0]], kv[1]) + } else if len(kv) == 1 { + out[kv[0]] = append(out[kv[0]], "") + } + } + return out +} + +// ExtractSingleBoolCommentTag parses comments for lines of the form: +// +// 'marker' + "key=value1" +// +// If the tag is not found, the default value is returned. Values are asserted +// to be boolean ("true" or "false"), and any other value will cause an error +// to be returned. If the key has multiple values, the first one will be used. +func ExtractSingleBoolCommentTag(marker string, key string, defaultVal bool, lines []string) (bool, error) { + values := ExtractCommentTags(marker, lines)[key] + if values == nil { + return defaultVal, nil + } + if values[0] == "true" { + return true, nil + } + if values[0] == "false" { + return false, nil + } + return false, fmt.Errorf("tag value for %q is not boolean: %q", key, values[0]) +} diff --git a/vendor/k8s.io/gengo/types/doc.go b/vendor/k8s.io/gengo/types/doc.go new file mode 100644 index 0000000000..74a969a763 --- /dev/null +++ b/vendor/k8s.io/gengo/types/doc.go @@ -0,0 +1,19 @@ +/* +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 types contains go type information, packaged in a way that makes +// auto-generation convenient, whether by template or straight go functions. +package types // import "k8s.io/gengo/types" diff --git a/vendor/k8s.io/gengo/types/flatten.go b/vendor/k8s.io/gengo/types/flatten.go new file mode 100644 index 0000000000..585014e8ba --- /dev/null +++ b/vendor/k8s.io/gengo/types/flatten.go @@ -0,0 +1,57 @@ +/* +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 types + +// FlattenMembers recursively takes any embedded members and puts them in the +// top level, correctly hiding them if the top level hides them. There must not +// be a cycle-- that implies infinite members. +// +// This is useful for e.g. computing all the valid keys in a json struct, +// properly considering any configuration of embedded structs. +func FlattenMembers(m []Member) []Member { + embedded := []Member{} + normal := []Member{} + type nameInfo struct { + top bool + i int + } + names := map[string]nameInfo{} + for i := range m { + if m[i].Embedded && m[i].Type.Kind == Struct { + embedded = append(embedded, m[i]) + } else { + normal = append(normal, m[i]) + names[m[i].Name] = nameInfo{true, len(normal) - 1} + } + } + for i := range embedded { + for _, e := range FlattenMembers(embedded[i].Type.Members) { + if info, found := names[e.Name]; found { + if info.top { + continue + } + if n := normal[info.i]; n.Name == e.Name && n.Type == e.Type { + continue + } + panic("conflicting members") + } + normal = append(normal, e) + names[e.Name] = nameInfo{false, len(normal) - 1} + } + } + return normal +} diff --git a/vendor/k8s.io/gengo/types/types.go b/vendor/k8s.io/gengo/types/types.go new file mode 100644 index 0000000000..ec25248e7e --- /dev/null +++ b/vendor/k8s.io/gengo/types/types.go @@ -0,0 +1,499 @@ +/* +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 types + +import "strings" + +// Ref makes a reference to the given type. It can only be used for e.g. +// passing to namers. +func Ref(packageName, typeName string) *Type { + return &Type{Name: Name{ + Name: typeName, + Package: packageName, + }} +} + +// A type name may have a package qualifier. +type Name struct { + // Empty if embedded or builtin. This is the package path unless Path is specified. + Package string + // The type name. + Name string + // An optional location of the type definition for languages that can have disjoint + // packages and paths. + Path string +} + +// String returns the name formatted as a string. +func (n Name) String() string { + if n.Package == "" { + return n.Name + } + return n.Package + "." + n.Name +} + +// ParseFullyQualifiedName parses a name like k8s.io/kubernetes/pkg/api.Pod into a Name. +func ParseFullyQualifiedName(fqn string) Name { + cs := strings.Split(fqn, ".") + pkg := "" + if len(cs) > 1 { + pkg = strings.Join(cs[0:len(cs)-1], ".") + } + return Name{ + Name: cs[len(cs)-1], + Package: pkg, + } +} + +// The possible classes of types. +type Kind string + +const ( + // Builtin is a primitive, like bool, string, int. + Builtin Kind = "Builtin" + Struct Kind = "Struct" + Map Kind = "Map" + Slice Kind = "Slice" + Pointer Kind = "Pointer" + + // Alias is an alias of another type, e.g. in: + // type Foo string + // type Bar Foo + // Bar is an alias of Foo. + // + // In the real go type system, Foo is a "Named" string; but to simplify + // generation, this type system will just say that Foo *is* a builtin. + // We then need "Alias" as a way for us to say that Bar *is* a Foo. + Alias Kind = "Alias" + + // Interface is any type that could have differing types at run time. + Interface Kind = "Interface" + + // The remaining types are included for completeness, but are not well + // supported. + Array Kind = "Array" // Array is just like slice, but has a fixed length. + Chan Kind = "Chan" + Func Kind = "Func" + + // DeclarationOf is different from other Kinds; it indicates that instead of + // representing an actual Type, the type is a declaration of an instance of + // a type. E.g., a top-level function, variable, or constant. See the + // comment for Type.Name for more detail. + DeclarationOf Kind = "DeclarationOf" + Unknown Kind = "" + Unsupported Kind = "Unsupported" + + // Protobuf is protobuf type. + Protobuf Kind = "Protobuf" +) + +// Package holds package-level information. +// Fields are public, as everything in this package, to enable consumption by +// templates (for example). But it is strongly encouraged for code to build by +// using the provided functions. +type Package struct { + // Canonical name of this package-- its path. + Path string + + // The location this package was loaded from + SourcePath string + + // Short name of this package; the name that appears in the + // 'package x' line. + Name string + + // The comment right above the package declaration in doc.go, if any. + DocComments []string + + // All comments from doc.go, if any. + // TODO: remove Comments and use DocComments everywhere. + Comments []string + + // Types within this package, indexed by their name (*not* including + // package name). + Types map[string]*Type + + // Functions within this package, indexed by their name (*not* including + // package name). + Functions map[string]*Type + + // Global variables within this package, indexed by their name (*not* including + // package name). + Variables map[string]*Type + + // Packages imported by this package, indexed by (canonicalized) + // package path. + Imports map[string]*Package +} + +// Has returns true if the given name references a type known to this package. +func (p *Package) Has(name string) bool { + _, has := p.Types[name] + return has +} + +// Type gets the given Type in this Package. If the Type is not already +// defined, this will add it and return the new Type value. The caller is +// expected to finish initialization. +func (p *Package) Type(typeName string) *Type { + if t, ok := p.Types[typeName]; ok { + return t + } + if p.Path == "" { + // Import the standard builtin types! + if t, ok := builtins.Types[typeName]; ok { + p.Types[typeName] = t + return t + } + } + t := &Type{Name: Name{Package: p.Path, Name: typeName}} + p.Types[typeName] = t + return t +} + +// Function gets the given function Type in this Package. If the function is +// not already defined, this will add it. If a function is added, it's the +// caller's responsibility to finish construction of the function by setting +// Underlying to the correct type. +func (p *Package) Function(funcName string) *Type { + if t, ok := p.Functions[funcName]; ok { + return t + } + t := &Type{Name: Name{Package: p.Path, Name: funcName}} + t.Kind = DeclarationOf + p.Functions[funcName] = t + return t +} + +// Variable gets the given variable Type in this Package. If the variable is +// not already defined, this will add it. If a variable is added, it's the caller's +// responsibility to finish construction of the variable by setting Underlying +// to the correct type. +func (p *Package) Variable(varName string) *Type { + if t, ok := p.Variables[varName]; ok { + return t + } + t := &Type{Name: Name{Package: p.Path, Name: varName}} + t.Kind = DeclarationOf + p.Variables[varName] = t + return t +} + +// HasImport returns true if p imports packageName. Package names include the +// package directory. +func (p *Package) HasImport(packageName string) bool { + _, has := p.Imports[packageName] + return has +} + +// Universe is a map of all packages. The key is the package name, but you +// should use Package(), Type(), Function(), or Variable() instead of direct +// access. +type Universe map[string]*Package + +// Type returns the canonical type for the given fully-qualified name. Builtin +// types will always be found, even if they haven't been explicitly added to +// the map. If a non-existing type is requested, this will create (a marker for) +// it. +func (u Universe) Type(n Name) *Type { + return u.Package(n.Package).Type(n.Name) +} + +// Function returns the canonical function for the given fully-qualified name. +// If a non-existing function is requested, this will create (a marker for) it. +// If a marker is created, it's the caller's responsibility to finish +// construction of the function by setting Underlying to the correct type. +func (u Universe) Function(n Name) *Type { + return u.Package(n.Package).Function(n.Name) +} + +// Variable returns the canonical variable for the given fully-qualified name. +// If a non-existing variable is requested, this will create (a marker for) it. +// If a marker is created, it's the caller's responsibility to finish +// construction of the variable by setting Underlying to the correct type. +func (u Universe) Variable(n Name) *Type { + return u.Package(n.Package).Variable(n.Name) +} + +// AddImports registers import lines for packageName. May be called multiple times. +// You are responsible for canonicalizing all package paths. +func (u Universe) AddImports(packagePath string, importPaths ...string) { + p := u.Package(packagePath) + for _, i := range importPaths { + p.Imports[i] = u.Package(i) + } +} + +// Package returns the Package for the given path. +// If a non-existing package is requested, this will create (a marker for) it. +// If a marker is created, it's the caller's responsibility to finish +// construction of the package. +func (u Universe) Package(packagePath string) *Package { + if p, ok := u[packagePath]; ok { + return p + } + p := &Package{ + Path: packagePath, + Types: map[string]*Type{}, + Functions: map[string]*Type{}, + Variables: map[string]*Type{}, + Imports: map[string]*Package{}, + } + u[packagePath] = p + return p +} + +// Type represents a subset of possible go types. +type Type struct { + // There are two general categories of types, those explicitly named + // and those anonymous. Named ones will have a non-empty package in the + // name field. + // + // An exception: If Kind == DeclarationOf, then this name is the name of a + // top-level function, variable, or const, and the type can be found in Underlying. + // We do this to allow the naming system to work against these objects, even + // though they aren't strictly speaking types. + Name Name + + // The general kind of this type. + Kind Kind + + // If there are comment lines immediately before the type definition, + // they will be recorded here. + CommentLines []string + + // If there are comment lines preceding the `CommentLines`, they will be + // recorded here. There are two cases: + // --- + // SecondClosestCommentLines + // a blank line + // CommentLines + // type definition + // --- + // + // or + // --- + // SecondClosestCommentLines + // a blank line + // type definition + // --- + SecondClosestCommentLines []string + + // If Kind == Struct + Members []Member + + // If Kind == Map, Slice, Pointer, or Chan + Elem *Type + + // If Kind == Map, this is the map's key type. + Key *Type + + // If Kind == Alias, this is the underlying type. + // If Kind == DeclarationOf, this is the type of the declaration. + Underlying *Type + + // If Kind == Interface, this is the set of all required functions. + // Otherwise, if this is a named type, this is the list of methods that + // type has. (All elements will have Kind=="Func") + Methods map[string]*Type + + // If Kind == func, this is the signature of the function. + Signature *Signature + + // TODO: Add: + // * channel direction + // * array length +} + +// String returns the name of the type. +func (t *Type) String() string { + return t.Name.String() +} + +// IsPrimitive returns whether the type is a built-in type or is an alias to a +// built-in type. For example: strings and aliases of strings are primitives, +// structs are not. +func (t *Type) IsPrimitive() bool { + if t.Kind == Builtin || (t.Kind == Alias && t.Underlying.Kind == Builtin) { + return true + } + return false +} + +// IsAssignable returns whether the type is deep-assignable. For example, +// slices and maps and pointers are shallow copies, but ints and strings are +// complete. +func (t *Type) IsAssignable() bool { + if t.IsPrimitive() { + return true + } + if t.Kind == Struct { + for _, m := range t.Members { + if !m.Type.IsAssignable() { + return false + } + } + return true + } + return false +} + +// IsAnonymousStruct returns true if the type is an anonymous struct or an alias +// to an anonymous struct. +func (t *Type) IsAnonymousStruct() bool { + return (t.Kind == Struct && t.Name.Name == "struct{}") || (t.Kind == Alias && t.Underlying.IsAnonymousStruct()) +} + +// A single struct member +type Member struct { + // The name of the member. + Name string + + // If the member is embedded (anonymous) this will be true, and the + // Name will be the type name. + Embedded bool + + // If there are comment lines immediately before the member in the type + // definition, they will be recorded here. + CommentLines []string + + // If there are tags along with this member, they will be saved here. + Tags string + + // The type of this member. + Type *Type +} + +// String returns the name and type of the member. +func (m Member) String() string { + return m.Name + " " + m.Type.String() +} + +// Signature is a function's signature. +type Signature struct { + // TODO: store the parameter names, not just types. + + // If a method of some type, this is the type it's a member of. + Receiver *Type + Parameters []*Type + Results []*Type + + // True if the last in parameter is of the form ...T. + Variadic bool + + // If there are comment lines immediately before this + // signature/method/function declaration, they will be recorded here. + CommentLines []string +} + +// Built in types. +var ( + String = &Type{ + Name: Name{Name: "string"}, + Kind: Builtin, + } + Int64 = &Type{ + Name: Name{Name: "int64"}, + Kind: Builtin, + } + Int32 = &Type{ + Name: Name{Name: "int32"}, + Kind: Builtin, + } + Int16 = &Type{ + Name: Name{Name: "int16"}, + Kind: Builtin, + } + Int = &Type{ + Name: Name{Name: "int"}, + Kind: Builtin, + } + Uint64 = &Type{ + Name: Name{Name: "uint64"}, + Kind: Builtin, + } + Uint32 = &Type{ + Name: Name{Name: "uint32"}, + Kind: Builtin, + } + Uint16 = &Type{ + Name: Name{Name: "uint16"}, + Kind: Builtin, + } + Uint = &Type{ + Name: Name{Name: "uint"}, + Kind: Builtin, + } + Uintptr = &Type{ + Name: Name{Name: "uintptr"}, + Kind: Builtin, + } + Float64 = &Type{ + Name: Name{Name: "float64"}, + Kind: Builtin, + } + Float32 = &Type{ + Name: Name{Name: "float32"}, + Kind: Builtin, + } + Float = &Type{ + Name: Name{Name: "float"}, + Kind: Builtin, + } + Bool = &Type{ + Name: Name{Name: "bool"}, + Kind: Builtin, + } + Byte = &Type{ + Name: Name{Name: "byte"}, + Kind: Builtin, + } + + builtins = &Package{ + Types: map[string]*Type{ + "bool": Bool, + "string": String, + "int": Int, + "int64": Int64, + "int32": Int32, + "int16": Int16, + "int8": Byte, + "uint": Uint, + "uint64": Uint64, + "uint32": Uint32, + "uint16": Uint16, + "uint8": Byte, + "uintptr": Uintptr, + "byte": Byte, + "float": Float, + "float64": Float64, + "float32": Float32, + }, + Imports: map[string]*Package{}, + Path: "", + Name: "", + } +) + +func IsInteger(t *Type) bool { + switch t { + case Int, Int64, Int32, Int16, Uint, Uint64, Uint32, Uint16, Byte: + return true + default: + return false + } +} diff --git a/vendor/k8s.io/klog/.travis.yml b/vendor/k8s.io/klog/.travis.yml new file mode 100644 index 0000000000..0f508dae66 --- /dev/null +++ b/vendor/k8s.io/klog/.travis.yml @@ -0,0 +1,15 @@ +language: go +go_import_path: k8s.io/klog +dist: xenial +go: + - 1.9.x + - 1.10.x + - 1.11.x +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - diff -u <(echo -n) <(golint $(go list -e ./...)) + - go tool vet . + - go test -v -race ./... +install: + - go get golang.org/x/lint/golint diff --git a/vendor/k8s.io/klog/CONTRIBUTING.md b/vendor/k8s.io/klog/CONTRIBUTING.md new file mode 100644 index 0000000000..574a56abbb --- /dev/null +++ b/vendor/k8s.io/klog/CONTRIBUTING.md @@ -0,0 +1,22 @@ +# Contributing Guidelines + +Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: + +_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ + +## Getting Started + +We have full documentation on how to get started contributing here: + +- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests +- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing) +- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet.md) - Common resources for existing developers + +## Mentorship + +- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers! + +## Contact Information + +- [Slack](https://kubernetes.slack.com/messages/sig-architecture) +- [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-architecture) diff --git a/vendor/k8s.io/klog/LICENSE b/vendor/k8s.io/klog/LICENSE new file mode 100644 index 0000000000..37ec93a14f --- /dev/null +++ b/vendor/k8s.io/klog/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/k8s.io/klog/OWNERS b/vendor/k8s.io/klog/OWNERS new file mode 100644 index 0000000000..d0168e8ca6 --- /dev/null +++ b/vendor/k8s.io/klog/OWNERS @@ -0,0 +1,11 @@ +# See the OWNERS docs at https://go.k8s.io/owners + +approvers: + - dims + - thockin + - justinsb + - tallclair + - piosz + - brancz + - DirectXMan12 + - lavalamp diff --git a/vendor/k8s.io/klog/README.md b/vendor/k8s.io/klog/README.md new file mode 100644 index 0000000000..6cb6d16837 --- /dev/null +++ b/vendor/k8s.io/klog/README.md @@ -0,0 +1,77 @@ +klog +==== + +klog is a permanant fork of https://github.com/golang/glog. original README from glog is below + +---- + +How to use klog +=============== +- Replace imports for `github.com/golang/glog` with `k8s.io/klog` +- Use `klog.InitFlags(nil)` explicitly for initializing global flags as we no longer use `init()` method to register the flags +- You can now use `log-file` instead of `log-dir` for logging to a single file (See `examples/log_file/usage_log_file.go`) +- If you want to redirect everything logged using klog somewhere else (say syslog!), you can use `klog.SetOutput()` method and supply a `io.Writer`. (See `examples/set_output/usage_set_output.go`) +- For more logging conventions (See [Logging Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/logging.md)) + +### Coexisting with glog +This package can be used side by side with glog. [This example](examples/coexist_glog/coexist_glog.go) shows how to initialize and syncronize flags from the global `flag.CommandLine` FlagSet. In addition, the example makes use of stderr as combined output by setting `alsologtostderr` (or `logtostderr`) to `true`. + +## Community, discussion, contribution, and support + +Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/). + +You can reach the maintainers of this project at: + +- [Slack](https://kubernetes.slack.com/messages/sig-architecture) +- [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-architecture) + +### Code of conduct + +Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md). + +---- + +glog +==== + +Leveled execution logs for Go. + +This is an efficient pure Go implementation of leveled logs in the +manner of the open source C++ package + https://github.com/google/glog + +By binding methods to booleans it is possible to use the log package +without paying the expense of evaluating the arguments to the log. +Through the -vmodule flag, the package also provides fine-grained +control over logging at the file level. + +The comment from glog.go introduces the ideas: + + Package glog implements logging analogous to the Google-internal + C++ INFO/ERROR/V setup. It provides functions Info, Warning, + Error, Fatal, plus formatting variants such as Infof. It + also provides V-style logging controlled by the -v and + -vmodule=file=2 flags. + + Basic examples: + + glog.Info("Prepare to repel boarders") + + glog.Fatalf("Initialization failed: %s", err) + + See the documentation for the V function for an explanation + of these examples: + + if glog.V(2) { + glog.Info("Starting transaction...") + } + + glog.V(2).Infoln("Processed", nItems, "elements") + + +The repository contains an open source version of the log package +used inside Google. The master copy of the source lives inside +Google, not here. The code in this repo is for export only and is not itself +under development. Feature requests will be ignored. + +Send bug reports to golang-nuts@googlegroups.com. diff --git a/vendor/k8s.io/klog/RELEASE.md b/vendor/k8s.io/klog/RELEASE.md new file mode 100644 index 0000000000..b53eb960ce --- /dev/null +++ b/vendor/k8s.io/klog/RELEASE.md @@ -0,0 +1,9 @@ +# Release Process + +The `klog` is released on an as-needed basis. The process is as follows: + +1. An issue is proposing a new release with a changelog since the last release +1. All [OWNERS](OWNERS) must LGTM this release +1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes the tag with `git push $VERSION` +1. The release issue is closed +1. An announcement email is sent to `kubernetes-dev@googlegroups.com` with the subject `[ANNOUNCE] kubernetes-template-project $VERSION is released` diff --git a/vendor/k8s.io/klog/SECURITY_CONTACTS b/vendor/k8s.io/klog/SECURITY_CONTACTS new file mode 100644 index 0000000000..520ddb5257 --- /dev/null +++ b/vendor/k8s.io/klog/SECURITY_CONTACTS @@ -0,0 +1,20 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +dims +thockin +justinsb +tallclair +piosz +brancz +DirectXMan12 +lavalamp diff --git a/vendor/k8s.io/klog/code-of-conduct.md b/vendor/k8s.io/klog/code-of-conduct.md new file mode 100644 index 0000000000..0d15c00cf3 --- /dev/null +++ b/vendor/k8s.io/klog/code-of-conduct.md @@ -0,0 +1,3 @@ +# Kubernetes Community Code of Conduct + +Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) diff --git a/vendor/k8s.io/klog/klog.go b/vendor/k8s.io/klog/klog.go new file mode 100644 index 0000000000..733d14b731 --- /dev/null +++ b/vendor/k8s.io/klog/klog.go @@ -0,0 +1,1241 @@ +// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package klog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. +// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as +// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags. +// +// Basic examples: +// +// glog.Info("Prepare to repel boarders") +// +// glog.Fatalf("Initialization failed: %s", err) +// +// See the documentation for the V function for an explanation of these examples: +// +// if glog.V(2) { +// glog.Info("Starting transaction...") +// } +// +// glog.V(2).Infoln("Processed", nItems, "elements") +// +// Log output is buffered and written periodically using Flush. Programs +// should call Flush before exiting to guarantee all log output is written. +// +// By default, all log statements write to files in a temporary directory. +// This package provides several flags that modify this behavior. +// As a result, flag.Parse must be called before any logging is done. +// +// -logtostderr=false +// Logs are written to standard error instead of to files. +// -alsologtostderr=false +// Logs are written to standard error as well as to files. +// -stderrthreshold=INFO +// Log events at or above this severity are logged to standard +// error as well as to files. +// -log_dir="" +// Log files will be written to this directory instead of the +// default temporary directory. +// +// Other flags provide aids to debugging. +// +// -log_backtrace_at="" +// When set to a file and line number holding a logging statement, +// such as +// -log_backtrace_at=gopherflakes.go:234 +// a stack trace will be written to the Info log whenever execution +// hits that statement. (Unlike with -vmodule, the ".go" must be +// present.) +// -v=0 +// Enable V-leveled logging at the specified level. +// -vmodule="" +// The syntax of the argument is a comma-separated list of pattern=N, +// where pattern is a literal file name (minus the ".go" suffix) or +// "glob" pattern and N is a V level. For instance, +// -vmodule=gopher*=3 +// sets the V level to 3 in all Go files whose names begin "gopher". +// +package klog + +import ( + "bufio" + "bytes" + "errors" + "flag" + "fmt" + "io" + stdLog "log" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" +) + +// severity identifies the sort of log: info, warning etc. It also implements +// the flag.Value interface. The -stderrthreshold flag is of type severity and +// should be modified only through the flag.Value interface. The values match +// the corresponding constants in C++. +type severity int32 // sync/atomic int32 + +// These constants identify the log levels in order of increasing severity. +// A message written to a high-severity log file is also written to each +// lower-severity log file. +const ( + infoLog severity = iota + warningLog + errorLog + fatalLog + numSeverity = 4 +) + +const severityChar = "IWEF" + +var severityName = []string{ + infoLog: "INFO", + warningLog: "WARNING", + errorLog: "ERROR", + fatalLog: "FATAL", +} + +// get returns the value of the severity. +func (s *severity) get() severity { + return severity(atomic.LoadInt32((*int32)(s))) +} + +// set sets the value of the severity. +func (s *severity) set(val severity) { + atomic.StoreInt32((*int32)(s), int32(val)) +} + +// String is part of the flag.Value interface. +func (s *severity) String() string { + return strconv.FormatInt(int64(*s), 10) +} + +// Get is part of the flag.Value interface. +func (s *severity) Get() interface{} { + return *s +} + +// Set is part of the flag.Value interface. +func (s *severity) Set(value string) error { + var threshold severity + // Is it a known name? + if v, ok := severityByName(value); ok { + threshold = v + } else { + v, err := strconv.Atoi(value) + if err != nil { + return err + } + threshold = severity(v) + } + logging.stderrThreshold.set(threshold) + return nil +} + +func severityByName(s string) (severity, bool) { + s = strings.ToUpper(s) + for i, name := range severityName { + if name == s { + return severity(i), true + } + } + return 0, false +} + +// OutputStats tracks the number of output lines and bytes written. +type OutputStats struct { + lines int64 + bytes int64 +} + +// Lines returns the number of lines written. +func (s *OutputStats) Lines() int64 { + return atomic.LoadInt64(&s.lines) +} + +// Bytes returns the number of bytes written. +func (s *OutputStats) Bytes() int64 { + return atomic.LoadInt64(&s.bytes) +} + +// Stats tracks the number of lines of output and number of bytes +// per severity level. Values must be read with atomic.LoadInt64. +var Stats struct { + Info, Warning, Error OutputStats +} + +var severityStats = [numSeverity]*OutputStats{ + infoLog: &Stats.Info, + warningLog: &Stats.Warning, + errorLog: &Stats.Error, +} + +// Level is exported because it appears in the arguments to V and is +// the type of the v flag, which can be set programmatically. +// It's a distinct type because we want to discriminate it from logType. +// Variables of type level are only changed under logging.mu. +// The -v flag is read only with atomic ops, so the state of the logging +// module is consistent. + +// Level is treated as a sync/atomic int32. + +// Level specifies a level of verbosity for V logs. *Level implements +// flag.Value; the -v flag is of type Level and should be modified +// only through the flag.Value interface. +type Level int32 + +// get returns the value of the Level. +func (l *Level) get() Level { + return Level(atomic.LoadInt32((*int32)(l))) +} + +// set sets the value of the Level. +func (l *Level) set(val Level) { + atomic.StoreInt32((*int32)(l), int32(val)) +} + +// String is part of the flag.Value interface. +func (l *Level) String() string { + return strconv.FormatInt(int64(*l), 10) +} + +// Get is part of the flag.Value interface. +func (l *Level) Get() interface{} { + return *l +} + +// Set is part of the flag.Value interface. +func (l *Level) Set(value string) error { + v, err := strconv.Atoi(value) + if err != nil { + return err + } + logging.mu.Lock() + defer logging.mu.Unlock() + logging.setVState(Level(v), logging.vmodule.filter, false) + return nil +} + +// moduleSpec represents the setting of the -vmodule flag. +type moduleSpec struct { + filter []modulePat +} + +// modulePat contains a filter for the -vmodule flag. +// It holds a verbosity level and a file pattern to match. +type modulePat struct { + pattern string + literal bool // The pattern is a literal string + level Level +} + +// match reports whether the file matches the pattern. It uses a string +// comparison if the pattern contains no metacharacters. +func (m *modulePat) match(file string) bool { + if m.literal { + return file == m.pattern + } + match, _ := filepath.Match(m.pattern, file) + return match +} + +func (m *moduleSpec) String() string { + // Lock because the type is not atomic. TODO: clean this up. + logging.mu.Lock() + defer logging.mu.Unlock() + var b bytes.Buffer + for i, f := range m.filter { + if i > 0 { + b.WriteRune(',') + } + fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) + } + return b.String() +} + +// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the +// struct is not exported. +func (m *moduleSpec) Get() interface{} { + return nil +} + +var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") + +// Syntax: -vmodule=recordio=2,file=1,gfs*=3 +func (m *moduleSpec) Set(value string) error { + var filter []modulePat + for _, pat := range strings.Split(value, ",") { + if len(pat) == 0 { + // Empty strings such as from a trailing comma can be ignored. + continue + } + patLev := strings.Split(pat, "=") + if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { + return errVmoduleSyntax + } + pattern := patLev[0] + v, err := strconv.Atoi(patLev[1]) + if err != nil { + return errors.New("syntax error: expect comma-separated list of filename=N") + } + if v < 0 { + return errors.New("negative value for vmodule level") + } + if v == 0 { + continue // Ignore. It's harmless but no point in paying the overhead. + } + // TODO: check syntax of filter? + filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) + } + logging.mu.Lock() + defer logging.mu.Unlock() + logging.setVState(logging.verbosity, filter, true) + return nil +} + +// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters +// that require filepath.Match to be called to match the pattern. +func isLiteral(pattern string) bool { + return !strings.ContainsAny(pattern, `\*?[]`) +} + +// traceLocation represents the setting of the -log_backtrace_at flag. +type traceLocation struct { + file string + line int +} + +// isSet reports whether the trace location has been specified. +// logging.mu is held. +func (t *traceLocation) isSet() bool { + return t.line > 0 +} + +// match reports whether the specified file and line matches the trace location. +// The argument file name is the full path, not the basename specified in the flag. +// logging.mu is held. +func (t *traceLocation) match(file string, line int) bool { + if t.line != line { + return false + } + if i := strings.LastIndex(file, "/"); i >= 0 { + file = file[i+1:] + } + return t.file == file +} + +func (t *traceLocation) String() string { + // Lock because the type is not atomic. TODO: clean this up. + logging.mu.Lock() + defer logging.mu.Unlock() + return fmt.Sprintf("%s:%d", t.file, t.line) +} + +// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the +// struct is not exported +func (t *traceLocation) Get() interface{} { + return nil +} + +var errTraceSyntax = errors.New("syntax error: expect file.go:234") + +// Syntax: -log_backtrace_at=gopherflakes.go:234 +// Note that unlike vmodule the file extension is included here. +func (t *traceLocation) Set(value string) error { + if value == "" { + // Unset. + t.line = 0 + t.file = "" + } + fields := strings.Split(value, ":") + if len(fields) != 2 { + return errTraceSyntax + } + file, line := fields[0], fields[1] + if !strings.Contains(file, ".") { + return errTraceSyntax + } + v, err := strconv.Atoi(line) + if err != nil { + return errTraceSyntax + } + if v <= 0 { + return errors.New("negative or zero value for level") + } + logging.mu.Lock() + defer logging.mu.Unlock() + t.line = v + t.file = file + return nil +} + +// flushSyncWriter is the interface satisfied by logging destinations. +type flushSyncWriter interface { + Flush() error + Sync() error + io.Writer +} + +func init() { + // Default stderrThreshold is INFO. + logging.stderrThreshold = infoLog + + logging.setVState(0, nil, false) + go logging.flushDaemon() +} + +// InitFlags is for explicitly initializing the flags +func InitFlags(flagset *flag.FlagSet) { + if flagset == nil { + flagset = flag.CommandLine + } + flagset.StringVar(&logging.logDir, "log_dir", "", "If non-empty, write log files in this directory") + flagset.StringVar(&logging.logFile, "log_file", "", "If non-empty, use this log file") + flagset.BoolVar(&logging.toStderr, "logtostderr", true, "log to standard error instead of files") + flagset.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") + flagset.Var(&logging.verbosity, "v", "number for the log level verbosity") + flagset.BoolVar(&logging.skipHeaders, "skip_headers", false, "If true, avoid header prefixes in the log messages") + flagset.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") + flagset.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") + flagset.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") +} + +// Flush flushes all pending log I/O. +func Flush() { + logging.lockAndFlushAll() +} + +// loggingT collects all the global state of the logging setup. +type loggingT struct { + // Boolean flags. Not handled atomically because the flag.Value interface + // does not let us avoid the =true, and that shorthand is necessary for + // compatibility. TODO: does this matter enough to fix? Seems unlikely. + toStderr bool // The -logtostderr flag. + alsoToStderr bool // The -alsologtostderr flag. + + // Level flag. Handled atomically. + stderrThreshold severity // The -stderrthreshold flag. + + // freeList is a list of byte buffers, maintained under freeListMu. + freeList *buffer + // freeListMu maintains the free list. It is separate from the main mutex + // so buffers can be grabbed and printed to without holding the main lock, + // for better parallelization. + freeListMu sync.Mutex + + // mu protects the remaining elements of this structure and is + // used to synchronize logging. + mu sync.Mutex + // file holds writer for each of the log types. + file [numSeverity]flushSyncWriter + // pcs is used in V to avoid an allocation when computing the caller's PC. + pcs [1]uintptr + // vmap is a cache of the V Level for each V() call site, identified by PC. + // It is wiped whenever the vmodule flag changes state. + vmap map[uintptr]Level + // filterLength stores the length of the vmodule filter chain. If greater + // than zero, it means vmodule is enabled. It may be read safely + // using sync.LoadInt32, but is only modified under mu. + filterLength int32 + // traceLocation is the state of the -log_backtrace_at flag. + traceLocation traceLocation + // These flags are modified only under lock, although verbosity may be fetched + // safely using atomic.LoadInt32. + vmodule moduleSpec // The state of the -vmodule flag. + verbosity Level // V logging level, the value of the -v flag/ + + // If non-empty, overrides the choice of directory in which to write logs. + // See createLogDirs for the full list of possible destinations. + logDir string + + // If non-empty, specifies the path of the file to write logs. mutually exclusive + // with the log-dir option. + logFile string + + // If true, do not add the prefix headers, useful when used with SetOutput + skipHeaders bool +} + +// buffer holds a byte Buffer for reuse. The zero value is ready for use. +type buffer struct { + bytes.Buffer + tmp [64]byte // temporary byte array for creating headers. + next *buffer +} + +var logging loggingT + +// setVState sets a consistent state for V logging. +// l.mu is held. +func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) { + // Turn verbosity off so V will not fire while we are in transition. + logging.verbosity.set(0) + // Ditto for filter length. + atomic.StoreInt32(&logging.filterLength, 0) + + // Set the new filters and wipe the pc->Level map if the filter has changed. + if setFilter { + logging.vmodule.filter = filter + logging.vmap = make(map[uintptr]Level) + } + + // Things are consistent now, so enable filtering and verbosity. + // They are enabled in order opposite to that in V. + atomic.StoreInt32(&logging.filterLength, int32(len(filter))) + logging.verbosity.set(verbosity) +} + +// getBuffer returns a new, ready-to-use buffer. +func (l *loggingT) getBuffer() *buffer { + l.freeListMu.Lock() + b := l.freeList + if b != nil { + l.freeList = b.next + } + l.freeListMu.Unlock() + if b == nil { + b = new(buffer) + } else { + b.next = nil + b.Reset() + } + return b +} + +// putBuffer returns a buffer to the free list. +func (l *loggingT) putBuffer(b *buffer) { + if b.Len() >= 256 { + // Let big buffers die a natural death. + return + } + l.freeListMu.Lock() + b.next = l.freeList + l.freeList = b + l.freeListMu.Unlock() +} + +var timeNow = time.Now // Stubbed out for testing. + +/* +header formats a log header as defined by the C++ implementation. +It returns a buffer containing the formatted header and the user's file and line number. +The depth specifies how many stack frames above lives the source line to be identified in the log message. + +Log lines have this form: + Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... +where the fields are defined as follows: + L A single character, representing the log level (eg 'I' for INFO) + mm The month (zero padded; ie May is '05') + dd The day (zero padded) + hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds + threadid The space-padded thread ID as returned by GetTID() + file The file name + line The line number + msg The user-supplied message +*/ +func (l *loggingT) header(s severity, depth int) (*buffer, string, int) { + _, file, line, ok := runtime.Caller(3 + depth) + if !ok { + file = "???" + line = 1 + } else { + slash := strings.LastIndex(file, "/") + if slash >= 0 { + file = file[slash+1:] + } + } + return l.formatHeader(s, file, line), file, line +} + +// formatHeader formats a log header using the provided file name and line number. +func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { + now := timeNow() + if line < 0 { + line = 0 // not a real line number, but acceptable to someDigits + } + if s > fatalLog { + s = infoLog // for safety. + } + buf := l.getBuffer() + if l.skipHeaders { + return buf + } + + // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. + // It's worth about 3X. Fprintf is hard. + _, month, day := now.Date() + hour, minute, second := now.Clock() + // Lmmdd hh:mm:ss.uuuuuu threadid file:line] + buf.tmp[0] = severityChar[s] + buf.twoDigits(1, int(month)) + buf.twoDigits(3, day) + buf.tmp[5] = ' ' + buf.twoDigits(6, hour) + buf.tmp[8] = ':' + buf.twoDigits(9, minute) + buf.tmp[11] = ':' + buf.twoDigits(12, second) + buf.tmp[14] = '.' + buf.nDigits(6, 15, now.Nanosecond()/1000, '0') + buf.tmp[21] = ' ' + buf.nDigits(7, 22, pid, ' ') // TODO: should be TID + buf.tmp[29] = ' ' + buf.Write(buf.tmp[:30]) + buf.WriteString(file) + buf.tmp[0] = ':' + n := buf.someDigits(1, line) + buf.tmp[n+1] = ']' + buf.tmp[n+2] = ' ' + buf.Write(buf.tmp[:n+3]) + return buf +} + +// Some custom tiny helper functions to print the log header efficiently. + +const digits = "0123456789" + +// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i]. +func (buf *buffer) twoDigits(i, d int) { + buf.tmp[i+1] = digits[d%10] + d /= 10 + buf.tmp[i] = digits[d%10] +} + +// nDigits formats an n-digit integer at buf.tmp[i], +// padding with pad on the left. +// It assumes d >= 0. +func (buf *buffer) nDigits(n, i, d int, pad byte) { + j := n - 1 + for ; j >= 0 && d > 0; j-- { + buf.tmp[i+j] = digits[d%10] + d /= 10 + } + for ; j >= 0; j-- { + buf.tmp[i+j] = pad + } +} + +// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i]. +func (buf *buffer) someDigits(i, d int) int { + // Print into the top, then copy down. We know there's space for at least + // a 10-digit number. + j := len(buf.tmp) + for { + j-- + buf.tmp[j] = digits[d%10] + d /= 10 + if d == 0 { + break + } + } + return copy(buf.tmp[i:], buf.tmp[j:]) +} + +func (l *loggingT) println(s severity, args ...interface{}) { + buf, file, line := l.header(s, 0) + fmt.Fprintln(buf, args...) + l.output(s, buf, file, line, false) +} + +func (l *loggingT) print(s severity, args ...interface{}) { + l.printDepth(s, 1, args...) +} + +func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) { + buf, file, line := l.header(s, depth) + fmt.Fprint(buf, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, false) +} + +func (l *loggingT) printf(s severity, format string, args ...interface{}) { + buf, file, line := l.header(s, 0) + fmt.Fprintf(buf, format, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, false) +} + +// printWithFileLine behaves like print but uses the provided file and line number. If +// alsoLogToStderr is true, the log message always appears on standard error; it +// will also appear in the log file unless --logtostderr is set. +func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) { + buf := l.formatHeader(s, file, line) + fmt.Fprint(buf, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, alsoToStderr) +} + +// redirectBuffer is used to set an alternate destination for the logs +type redirectBuffer struct { + w io.Writer +} + +func (rb *redirectBuffer) Sync() error { + return nil +} + +func (rb *redirectBuffer) Flush() error { + return nil +} + +func (rb *redirectBuffer) Write(bytes []byte) (n int, err error) { + return rb.w.Write(bytes) +} + +// SetOutput sets the output destination for all severities +func SetOutput(w io.Writer) { + for s := fatalLog; s >= infoLog; s-- { + rb := &redirectBuffer{ + w: w, + } + logging.file[s] = rb + } +} + +// SetOutputBySeverity sets the output destination for specific severity +func SetOutputBySeverity(name string, w io.Writer) { + sev, ok := severityByName(name) + if !ok { + panic(fmt.Sprintf("SetOutputBySeverity(%q): unrecognized severity name", name)) + } + rb := &redirectBuffer{ + w: w, + } + logging.file[sev] = rb +} + +// output writes the data to the log files and releases the buffer. +func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) { + l.mu.Lock() + if l.traceLocation.isSet() { + if l.traceLocation.match(file, line) { + buf.Write(stacks(false)) + } + } + data := buf.Bytes() + if l.toStderr { + if s >= l.stderrThreshold.get() { + os.Stderr.Write(data) + } + } else { + if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() { + os.Stderr.Write(data) + } + if l.file[s] == nil { + if err := l.createFiles(s); err != nil { + os.Stderr.Write(data) // Make sure the message appears somewhere. + l.exit(err) + } + } + switch s { + case fatalLog: + l.file[fatalLog].Write(data) + fallthrough + case errorLog: + l.file[errorLog].Write(data) + fallthrough + case warningLog: + l.file[warningLog].Write(data) + fallthrough + case infoLog: + l.file[infoLog].Write(data) + } + } + if s == fatalLog { + // If we got here via Exit rather than Fatal, print no stacks. + if atomic.LoadUint32(&fatalNoStacks) > 0 { + l.mu.Unlock() + timeoutFlush(10 * time.Second) + os.Exit(1) + } + // Dump all goroutine stacks before exiting. + // First, make sure we see the trace for the current goroutine on standard error. + // If -logtostderr has been specified, the loop below will do that anyway + // as the first stack in the full dump. + if !l.toStderr { + os.Stderr.Write(stacks(false)) + } + // Write the stack trace for all goroutines to the files. + trace := stacks(true) + logExitFunc = func(error) {} // If we get a write error, we'll still exit below. + for log := fatalLog; log >= infoLog; log-- { + if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set. + f.Write(trace) + } + } + l.mu.Unlock() + timeoutFlush(10 * time.Second) + os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway. + } + l.putBuffer(buf) + l.mu.Unlock() + if stats := severityStats[s]; stats != nil { + atomic.AddInt64(&stats.lines, 1) + atomic.AddInt64(&stats.bytes, int64(len(data))) + } +} + +// timeoutFlush calls Flush and returns when it completes or after timeout +// elapses, whichever happens first. This is needed because the hooks invoked +// by Flush may deadlock when glog.Fatal is called from a hook that holds +// a lock. +func timeoutFlush(timeout time.Duration) { + done := make(chan bool, 1) + go func() { + Flush() // calls logging.lockAndFlushAll() + done <- true + }() + select { + case <-done: + case <-time.After(timeout): + fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout) + } +} + +// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. +func stacks(all bool) []byte { + // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. + n := 10000 + if all { + n = 100000 + } + var trace []byte + for i := 0; i < 5; i++ { + trace = make([]byte, n) + nbytes := runtime.Stack(trace, all) + if nbytes < len(trace) { + return trace[:nbytes] + } + n *= 2 + } + return trace +} + +// logExitFunc provides a simple mechanism to override the default behavior +// of exiting on error. Used in testing and to guarantee we reach a required exit +// for fatal logs. Instead, exit could be a function rather than a method but that +// would make its use clumsier. +var logExitFunc func(error) + +// exit is called if there is trouble creating or writing log files. +// It flushes the logs and exits the program; there's no point in hanging around. +// l.mu is held. +func (l *loggingT) exit(err error) { + fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err) + // If logExitFunc is set, we do that instead of exiting. + if logExitFunc != nil { + logExitFunc(err) + return + } + l.flushAll() + os.Exit(2) +} + +// syncBuffer joins a bufio.Writer to its underlying file, providing access to the +// file's Sync method and providing a wrapper for the Write method that provides log +// file rotation. There are conflicting methods, so the file cannot be embedded. +// l.mu is held for all its methods. +type syncBuffer struct { + logger *loggingT + *bufio.Writer + file *os.File + sev severity + nbytes uint64 // The number of bytes written to this file +} + +func (sb *syncBuffer) Sync() error { + return sb.file.Sync() +} + +func (sb *syncBuffer) Write(p []byte) (n int, err error) { + if sb.nbytes+uint64(len(p)) >= MaxSize { + if err := sb.rotateFile(time.Now()); err != nil { + sb.logger.exit(err) + } + } + n, err = sb.Writer.Write(p) + sb.nbytes += uint64(n) + if err != nil { + sb.logger.exit(err) + } + return +} + +// rotateFile closes the syncBuffer's file and starts a new one. +func (sb *syncBuffer) rotateFile(now time.Time) error { + if sb.file != nil { + sb.Flush() + sb.file.Close() + } + var err error + sb.file, _, err = create(severityName[sb.sev], now) + sb.nbytes = 0 + if err != nil { + return err + } + + sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) + + // Write header. + var buf bytes.Buffer + fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) + fmt.Fprintf(&buf, "Running on machine: %s\n", host) + fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) + fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") + n, err := sb.file.Write(buf.Bytes()) + sb.nbytes += uint64(n) + return err +} + +// bufferSize sizes the buffer associated with each log file. It's large +// so that log records can accumulate without the logging thread blocking +// on disk I/O. The flushDaemon will block instead. +const bufferSize = 256 * 1024 + +// createFiles creates all the log files for severity from sev down to infoLog. +// l.mu is held. +func (l *loggingT) createFiles(sev severity) error { + now := time.Now() + // Files are created in decreasing severity order, so as soon as we find one + // has already been created, we can stop. + for s := sev; s >= infoLog && l.file[s] == nil; s-- { + sb := &syncBuffer{ + logger: l, + sev: s, + } + if err := sb.rotateFile(now); err != nil { + return err + } + l.file[s] = sb + } + return nil +} + +const flushInterval = 5 * time.Second + +// flushDaemon periodically flushes the log file buffers. +func (l *loggingT) flushDaemon() { + for range time.NewTicker(flushInterval).C { + l.lockAndFlushAll() + } +} + +// lockAndFlushAll is like flushAll but locks l.mu first. +func (l *loggingT) lockAndFlushAll() { + l.mu.Lock() + l.flushAll() + l.mu.Unlock() +} + +// flushAll flushes all the logs and attempts to "sync" their data to disk. +// l.mu is held. +func (l *loggingT) flushAll() { + // Flush from fatal down, in case there's trouble flushing. + for s := fatalLog; s >= infoLog; s-- { + file := l.file[s] + if file != nil { + file.Flush() // ignore error + file.Sync() // ignore error + } + } +} + +// CopyStandardLogTo arranges for messages written to the Go "log" package's +// default logs to also appear in the Google logs for the named and lower +// severities. Subsequent changes to the standard log's default output location +// or format may break this behavior. +// +// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not +// recognized, CopyStandardLogTo panics. +func CopyStandardLogTo(name string) { + sev, ok := severityByName(name) + if !ok { + panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name)) + } + // Set a log format that captures the user's file and line: + // d.go:23: message + stdLog.SetFlags(stdLog.Lshortfile) + stdLog.SetOutput(logBridge(sev)) +} + +// logBridge provides the Write method that enables CopyStandardLogTo to connect +// Go's standard logs to the logs provided by this package. +type logBridge severity + +// Write parses the standard logging line and passes its components to the +// logger for severity(lb). +func (lb logBridge) Write(b []byte) (n int, err error) { + var ( + file = "???" + line = 1 + text string + ) + // Split "d.go:23: message" into "d.go", "23", and "message". + if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 { + text = fmt.Sprintf("bad log format: %s", b) + } else { + file = string(parts[0]) + text = string(parts[2][1:]) // skip leading space + line, err = strconv.Atoi(string(parts[1])) + if err != nil { + text = fmt.Sprintf("bad line number: %s", b) + line = 1 + } + } + // printWithFileLine with alsoToStderr=true, so standard log messages + // always appear on standard error. + logging.printWithFileLine(severity(lb), file, line, true, text) + return len(b), nil +} + +// setV computes and remembers the V level for a given PC +// when vmodule is enabled. +// File pattern matching takes the basename of the file, stripped +// of its .go suffix, and uses filepath.Match, which is a little more +// general than the *? matching used in C++. +// l.mu is held. +func (l *loggingT) setV(pc uintptr) Level { + fn := runtime.FuncForPC(pc) + file, _ := fn.FileLine(pc) + // The file is something like /a/b/c/d.go. We want just the d. + if strings.HasSuffix(file, ".go") { + file = file[:len(file)-3] + } + if slash := strings.LastIndex(file, "/"); slash >= 0 { + file = file[slash+1:] + } + for _, filter := range l.vmodule.filter { + if filter.match(file) { + l.vmap[pc] = filter.level + return filter.level + } + } + l.vmap[pc] = 0 + return 0 +} + +// Verbose is a boolean type that implements Infof (like Printf) etc. +// See the documentation of V for more information. +type Verbose bool + +// V reports whether verbosity at the call site is at least the requested level. +// The returned value is a boolean of type Verbose, which implements Info, Infoln +// and Infof. These methods will write to the Info log if called. +// Thus, one may write either +// if glog.V(2) { glog.Info("log this") } +// or +// glog.V(2).Info("log this") +// The second form is shorter but the first is cheaper if logging is off because it does +// not evaluate its arguments. +// +// Whether an individual call to V generates a log record depends on the setting of +// the -v and --vmodule flags; both are off by default. If the level in the call to +// V is at least the value of -v, or of -vmodule for the source file containing the +// call, the V call will log. +func V(level Level) Verbose { + // This function tries hard to be cheap unless there's work to do. + // The fast path is two atomic loads and compares. + + // Here is a cheap but safe test to see if V logging is enabled globally. + if logging.verbosity.get() >= level { + return Verbose(true) + } + + // It's off globally but it vmodule may still be set. + // Here is another cheap but safe test to see if vmodule is enabled. + if atomic.LoadInt32(&logging.filterLength) > 0 { + // Now we need a proper lock to use the logging structure. The pcs field + // is shared so we must lock before accessing it. This is fairly expensive, + // but if V logging is enabled we're slow anyway. + logging.mu.Lock() + defer logging.mu.Unlock() + if runtime.Callers(2, logging.pcs[:]) == 0 { + return Verbose(false) + } + v, ok := logging.vmap[logging.pcs[0]] + if !ok { + v = logging.setV(logging.pcs[0]) + } + return Verbose(v >= level) + } + return Verbose(false) +} + +// Info is equivalent to the global Info function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Info(args ...interface{}) { + if v { + logging.print(infoLog, args...) + } +} + +// Infoln is equivalent to the global Infoln function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Infoln(args ...interface{}) { + if v { + logging.println(infoLog, args...) + } +} + +// Infof is equivalent to the global Infof function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Infof(format string, args ...interface{}) { + if v { + logging.printf(infoLog, format, args...) + } +} + +// Info logs to the INFO log. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Info(args ...interface{}) { + logging.print(infoLog, args...) +} + +// InfoDepth acts as Info but uses depth to determine which call frame to log. +// InfoDepth(0, "msg") is the same as Info("msg"). +func InfoDepth(depth int, args ...interface{}) { + logging.printDepth(infoLog, depth, args...) +} + +// Infoln logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Infoln(args ...interface{}) { + logging.println(infoLog, args...) +} + +// Infof logs to the INFO log. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Infof(format string, args ...interface{}) { + logging.printf(infoLog, format, args...) +} + +// Warning logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Warning(args ...interface{}) { + logging.print(warningLog, args...) +} + +// WarningDepth acts as Warning but uses depth to determine which call frame to log. +// WarningDepth(0, "msg") is the same as Warning("msg"). +func WarningDepth(depth int, args ...interface{}) { + logging.printDepth(warningLog, depth, args...) +} + +// Warningln logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Warningln(args ...interface{}) { + logging.println(warningLog, args...) +} + +// Warningf logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Warningf(format string, args ...interface{}) { + logging.printf(warningLog, format, args...) +} + +// Error logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Error(args ...interface{}) { + logging.print(errorLog, args...) +} + +// ErrorDepth acts as Error but uses depth to determine which call frame to log. +// ErrorDepth(0, "msg") is the same as Error("msg"). +func ErrorDepth(depth int, args ...interface{}) { + logging.printDepth(errorLog, depth, args...) +} + +// Errorln logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Errorln(args ...interface{}) { + logging.println(errorLog, args...) +} + +// Errorf logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Errorf(format string, args ...interface{}) { + logging.printf(errorLog, format, args...) +} + +// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Fatal(args ...interface{}) { + logging.print(fatalLog, args...) +} + +// FatalDepth acts as Fatal but uses depth to determine which call frame to log. +// FatalDepth(0, "msg") is the same as Fatal("msg"). +func FatalDepth(depth int, args ...interface{}) { + logging.printDepth(fatalLog, depth, args...) +} + +// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Fatalln(args ...interface{}) { + logging.println(fatalLog, args...) +} + +// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Fatalf(format string, args ...interface{}) { + logging.printf(fatalLog, format, args...) +} + +// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks. +// It allows Exit and relatives to use the Fatal logs. +var fatalNoStacks uint32 + +// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Exit(args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.print(fatalLog, args...) +} + +// ExitDepth acts as Exit but uses depth to determine which call frame to log. +// ExitDepth(0, "msg") is the same as Exit("msg"). +func ExitDepth(depth int, args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.printDepth(fatalLog, depth, args...) +} + +// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +func Exitln(args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.println(fatalLog, args...) +} + +// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Exitf(format string, args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.printf(fatalLog, format, args...) +} diff --git a/vendor/k8s.io/klog/klog_file.go b/vendor/k8s.io/klog/klog_file.go new file mode 100644 index 0000000000..b76a4e10be --- /dev/null +++ b/vendor/k8s.io/klog/klog_file.go @@ -0,0 +1,126 @@ +// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// File I/O for logs. + +package klog + +import ( + "errors" + "fmt" + "os" + "os/user" + "path/filepath" + "strings" + "sync" + "time" +) + +// MaxSize is the maximum size of a log file in bytes. +var MaxSize uint64 = 1024 * 1024 * 1800 + +// logDirs lists the candidate directories for new log files. +var logDirs []string + +func createLogDirs() { + if logging.logDir != "" { + logDirs = append(logDirs, logging.logDir) + } + logDirs = append(logDirs, os.TempDir()) +} + +var ( + pid = os.Getpid() + program = filepath.Base(os.Args[0]) + host = "unknownhost" + userName = "unknownuser" +) + +func init() { + h, err := os.Hostname() + if err == nil { + host = shortHostname(h) + } + + current, err := user.Current() + if err == nil { + userName = current.Username + } + + // Sanitize userName since it may contain filepath separators on Windows. + userName = strings.Replace(userName, `\`, "_", -1) +} + +// shortHostname returns its argument, truncating at the first period. +// For instance, given "www.google.com" it returns "www". +func shortHostname(hostname string) string { + if i := strings.Index(hostname, "."); i >= 0 { + return hostname[:i] + } + return hostname +} + +// logName returns a new log file name containing tag, with start time t, and +// the name for the symlink for tag. +func logName(tag string, t time.Time) (name, link string) { + name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", + program, + host, + userName, + tag, + t.Year(), + t.Month(), + t.Day(), + t.Hour(), + t.Minute(), + t.Second(), + pid) + return name, program + "." + tag +} + +var onceLogDirs sync.Once + +// create creates a new log file and returns the file and its filename, which +// contains tag ("INFO", "FATAL", etc.) and t. If the file is created +// successfully, create also attempts to update the symlink for that tag, ignoring +// errors. +func create(tag string, t time.Time) (f *os.File, filename string, err error) { + if logging.logFile != "" { + f, err := os.Create(logging.logFile) + if err == nil { + return f, logging.logFile, nil + } + return nil, "", fmt.Errorf("log: unable to create log: %v", err) + } + onceLogDirs.Do(createLogDirs) + if len(logDirs) == 0 { + return nil, "", errors.New("log: no log dirs") + } + name, link := logName(tag, t) + var lastErr error + for _, dir := range logDirs { + fname := filepath.Join(dir, name) + f, err := os.Create(fname) + if err == nil { + symlink := filepath.Join(dir, link) + os.Remove(symlink) // ignore err + os.Symlink(name, symlink) // ignore err + return f, fname, nil + } + lastErr = err + } + return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) +}