diff --git a/apis/voyager/v1beta1/certificate.go b/apis/voyager/v1beta1/certificate.go index c6d5a95ef..0acdac13c 100644 --- a/apis/voyager/v1beta1/certificate.go +++ b/apis/voyager/v1beta1/certificate.go @@ -6,9 +6,9 @@ import ( ) const ( - ResourceKindCertificate = "Certificate" - ResourceNameCertificate = "certificate" - ResourceTypeCertificate = "certificates" + ResourceKindCertificate = "Certificate" + ResourceSingularCertificate = "certificate" + ResourcePluralCertificate = "certificates" ) // +genclient diff --git a/apis/voyager/v1beta1/crd.go b/apis/voyager/v1beta1/crd.go index 2dc30bbe0..59c264aa0 100644 --- a/apis/voyager/v1beta1/crd.go +++ b/apis/voyager/v1beta1/crd.go @@ -1,49 +1,44 @@ package v1beta1 import ( - "github.com/appscode/voyager/apis/voyager" + crdutils "github.com/appscode/kutil/apiextensions/v1beta1" apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -const VoyagerFinalizer = "voyager.appscode.com" - func (r Ingress) CustomResourceDefinition() *apiextensions.CustomResourceDefinition { - return &apiextensions.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: ResourceTypeIngress + "." + SchemeGroupVersion.Group, - Labels: map[string]string{"app": "voyager"}, - }, - Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: voyager.GroupName, - Version: SchemeGroupVersion.Version, - Scope: apiextensions.NamespaceScoped, - Names: apiextensions.CustomResourceDefinitionNames{ - Singular: ResourceNameIngress, - Plural: ResourceTypeIngress, - Kind: ResourceKindIngress, - ShortNames: []string{"ing"}, - }, + return crdutils.NewCustomResourceDefinition(crdutils.Config{ + Group: SchemeGroupVersion.Group, + Version: SchemeGroupVersion.Version, + Plural: ResourcePluralIngress, + Singular: ResourceSingularIngress, + Kind: ResourceKindIngress, + ListKind: ResourceKindIngress + "List", + ShortNames: []string{"ing"}, + ResourceScope: string(apiextensions.NamespaceScoped), + Labels: crdutils.Labels{ + LabelsMap: map[string]string{"app": "voyager"}, }, - } + SpecDefinitionName: "github.com/appscode/voyager/apis/voyager/v1beta1.Ingress", + EnableValidation: true, + GetOpenAPIDefinitions: GetOpenAPIDefinitions, + }) } func (c Certificate) CustomResourceDefinition() *apiextensions.CustomResourceDefinition { - return &apiextensions.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: ResourceTypeCertificate + "." + SchemeGroupVersion.Group, - Labels: map[string]string{"app": "voyager"}, - }, - Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: voyager.GroupName, - Version: SchemeGroupVersion.Version, - Scope: apiextensions.NamespaceScoped, - Names: apiextensions.CustomResourceDefinitionNames{ - Singular: ResourceNameCertificate, - Plural: ResourceTypeCertificate, - Kind: ResourceKindCertificate, - ShortNames: []string{"cert"}, - }, + return crdutils.NewCustomResourceDefinition(crdutils.Config{ + Group: SchemeGroupVersion.Group, + Version: SchemeGroupVersion.Version, + Plural: ResourcePluralCertificate, + Singular: ResourceSingularCertificate, + Kind: ResourceKindCertificate, + ListKind: ResourceKindCertificate + "List", + ShortNames: []string{"cert"}, + ResourceScope: string(apiextensions.NamespaceScoped), + Labels: crdutils.Labels{ + LabelsMap: map[string]string{"app": "voyager"}, }, - } + SpecDefinitionName: "github.com/appscode/voyager/apis/voyager/v1beta1.Certificate", + EnableValidation: true, + GetOpenAPIDefinitions: GetOpenAPIDefinitions, + }) } diff --git a/apis/voyager/v1beta1/crds.yaml b/apis/voyager/v1beta1/crds.yaml index 293b0f7b6..d61cc0344 100644 --- a/apis/voyager/v1beta1/crds.yaml +++ b/apis/voyager/v1beta1/crds.yaml @@ -1,35 +1,549 @@ +--- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: certificates.voyager.appscode.com + creationTimestamp: null labels: app: voyager + name: ingresses.voyager.appscode.com spec: group: voyager.appscode.com names: - kind: Certificate - listKind: CertificateList - plural: certificates + kind: Ingress + listKind: IngressList + plural: ingresses shortNames: - - cert - singular: certificate + - ing + singular: ingress scope: Namespaced + validation: + openAPIV3Schema: + description: Custom Ingress type for Voyager. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + metadata: {} + spec: + description: IngressSpec describes the Ingress the user wishes to exist. + properties: + affinity: {} + backend: + properties: + backendRules: + description: Serialized HAProxy rules to apply on server backend + including request, response or header rewrite. acls also can be + used. https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#1 + items: + type: string + type: array + headerRules: + description: |- + Header rules to modifies the header. + + Deprecated: Use backendRule, will be removed. + items: + type: string + type: array + hostNames: + description: Host names to forward traffic to. If empty traffic + will be forwarded to all subsets instance. If set only matched + hosts will get the traffic. This is an handy way to send traffic + to Specific StatefulSet pod. IE. Setting [web-0] will send traffic + to only web-0 host for this StatefulSet, https://kubernetes.io/docs/tasks/stateful-application/basic-stateful-set/#creating-a-statefulset + items: + type: string + type: array + name: + description: User can specify backend name for using it with custom + acl Otherwise it will be generated + type: string + rewriteRules: + description: |- + Path rewrite rules with haproxy formatted regex. + + Deprecated: Use backendRule, will be removed. + items: + type: string + type: array + serviceName: + description: Specifies the name of the referenced service. + type: string + servicePort: {} + externalIPs: + description: externalIPs is a list of IP addresses for which nodes in + the cluster will also accept traffic for this service. These IPs + are not managed by Kubernetes. The user is responsible for ensuring + that traffic arrives at a node with this IP. A common example is + external load-balancers that are not part of the Kubernetes system. + items: + type: string + type: array + frontendRules: + description: Frontend rules specifies a set of rules that should be + applied in HAProxy frontend configuration. The set of keywords are + from here https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.1 + Only frontend sections can be applied here. It is up to user to provide + valid set of rules. This allows acls or other options in frontend + sections in HAProxy config. Frontend rules will be mapped with Ingress + Rules according to port. + items: + properties: + auth: + properties: + basic: + properties: + realm: + type: string + secretName: + type: string + tls: + properties: + errorPage: + type: string + headers: + additionalProperties: + type: string + type: object + secretName: + type: string + verifyClient: + type: string + port: {} + rules: + description: Serialized rules + items: + type: string + type: array + type: array + imagePullSecrets: + description: 'ImagePullSecrets is an optional list of references to + secrets in the same namespace to use for pulling any of the images + used by this PodSpec. If specified, these secrets will be passed to + individual puller implementations for them to use. For example, in + the case of docker, only DockerConfig type secrets are honored. More + info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' + items: {} + type: array + loadBalancerSourceRanges: + description: 'Optional: If specified and supported by the platform, + this will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field will be + ignored if the cloud-provider does not support the feature. https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be true for the + pod to fit on a node. Selector which must match a node''s labels for + the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + resources: {} + rules: + description: A list of host rules used to configure the Ingress. If + unspecified, or no rule matches, all traffic is sent to the default + backend. + items: + description: IngressRule represents the rules mapping the paths under + a specified host to the related backend services. Incoming requests + are first evaluated for a host match, then routed to the backend + associated with the matching IngressRuleValue. + properties: + host: + description: "Host is the fully qualified domain name of a network + host, as defined by RFC 3986. Note the following deviations + from the \"host\" part of the URI as defined in the RFC: 1. + IPs are not allowed. Currently an IngressRuleValue can only + apply to the\n\t IP in the Spec of the parent Ingress.\n2. + The `:` delimiter is not respected because ports are not allowed.\n\t + \ Currently the port of an Ingress is implicitly :80 for http + and\n\t :443 for https.\nBoth these may change in the future. + Incoming requests are matched against the host before the IngressRuleValue. + If the host is unspecified, the Ingress routes all traffic based + on the specified IngressRuleValue." + type: string + http: + description: 'HTTPIngressRuleValue is a list of http selectors + pointing to backends. In the example: http:///? + -> backend where where parts of the url correspond to RFC 3986, + this resource will be used to match against everything after + the last ''/'' and before the first ''?'' or ''#''.' + properties: + address: + description: The network address to listen HTTP(s) connections + on. + type: string + noTLS: + description: Set noTLS = true to force plain text. Else, auto + detect like present + type: boolean + nodePort: {} + paths: + description: A collection of paths that map requests to backends. + items: + description: HTTPIngressPath associates a path regex with + a backend. Incoming urls matching the path are forwarded + to the backend. + properties: + backend: + properties: + backendRules: + description: Serialized HAProxy rules to apply on + server backend including request, response or + header rewrite. acls also can be used. https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#1 + items: + type: string + type: array + headerRules: + description: |- + Header rules to modifies the header. + + Deprecated: Use backendRule, will be removed. + items: + type: string + type: array + hostNames: + description: Host names to forward traffic to. If + empty traffic will be forwarded to all subsets + instance. If set only matched hosts will get the + traffic. This is an handy way to send traffic + to Specific StatefulSet pod. IE. Setting [web-0] + will send traffic to only web-0 host for this + StatefulSet, https://kubernetes.io/docs/tasks/stateful-application/basic-stateful-set/#creating-a-statefulset + items: + type: string + type: array + name: + description: User can specify backend name for using + it with custom acl Otherwise it will be generated + type: string + rewriteRules: + description: |- + Path rewrite rules with haproxy formatted regex. + + Deprecated: Use backendRule, will be removed. + items: + type: string + type: array + serviceName: + description: Specifies the name of the referenced + service. + type: string + servicePort: {} + path: + description: Path is a extended POSIX regex as defined + by IEEE Std 1003.1, (i.e this follows the egrep/unix + syntax, not the perl syntax) matched against the path + of an incoming request. Currently it can contain characters + disallowed from the conventional "path" part of a + URL as defined by RFC 3986. Paths must begin with + a '/'. If unspecified, the path defaults to a catch + all sending traffic to the backend. + type: string + type: array + port: {} + required: + - paths + tcp: + properties: + address: + description: The network address to listen TCP connections + on. + type: string + alpn: + description: Application-Layer Protocol Negotiation (ALPN) + is a Transport Layer Security (TLS) extension for application + layer protocol negotiation. ALPN allows the application + layer to negotiate which protocol should be performed over + a secure connection in a manner which avoids additional + round trips and which is independent of the application + layer protocols. It is used by HTTP/2. If provided a list + of alpn will be added to port as alpn option1,option2,... + If SecretName is Provided this secret will be used to terminate + SSL with alpn options. If Secret name is not provided backend + server is responsible for handling SSL. + items: + type: string + type: array + backend: + description: IngressBackend describes all endpoints for a + given service and port. + properties: + backendRules: + description: Serialized HAProxy rules to apply on server + backend including request, response or header rewrite. + acls also can be used. https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#1 + items: + type: string + type: array + hostNames: + description: Host names to forward traffic to. If empty + traffic will be forwarded to all subsets instance. If + set only matched hosts will get the traffic. This is + an handy way to send traffic to Specific StatefulSet + pod. IE. Setting [web-0] will send traffic to only web-0 + host for this StatefulSet, https://kubernetes.io/docs/tasks/stateful-application/basic-stateful-set/#creating-a-statefulset + items: + type: string + type: array + name: + description: User can specify backend name for using it + with custom acl Otherwise it will be generated + type: string + serviceName: + description: Specifies the name of the referenced service. + type: string + servicePort: {} + noTLS: + description: Set noTLS = true to force plain text. Else, auto + detect like present + type: boolean + nodePort: {} + port: {} + type: array + schedulerName: + description: If specified, the pod will be dispatched by specified scheduler. + If not specified, the pod will be dispatched by default scheduler. + type: string + tls: + description: TLS is the TLS configuration. Currently the Ingress only + supports a single TLS port, 443, and assumes TLS termination. If multiple + members of this list specify different hosts, they will be multiplexed + on the same port according to the hostname specified through the SNI + TLS extension. + items: + description: IngressTLS describes the transport layer security associated + with an Ingress. + properties: + hosts: + description: Hosts are a list of hosts included in the TLS certificate. + The values in this list must match the name/s used in the tlsSecret. + Defaults to the wildcard host setting for the loadbalancer controller + fulfilling this Ingress, if left unspecified. + items: + type: string + type: array + ref: + description: LocalTypedReference contains enough information to + let you inspect or modify the referred object. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + secretName: + description: SecretName is the name of the secret used to terminate + SSL traffic on 443. Field is left optional to allow SSL routing + based on SNI hostname alone. If the SNI host in a listener conflicts + with the "Host" header field used by an IngressRule, the SNI + host is used for termination and value of the Host header is + used for routing. Deprecated + type: string + type: array + tolerations: + description: If specified, the pod's tolerations. + items: {} + type: array + status: + description: IngressStatus describe the current state of the Ingress. + properties: + loadBalancer: {} version: v1beta1 +status: + acceptedNames: + kind: "" + plural: "" + conditions: null --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: ingresses.voyager.appscode.com + creationTimestamp: null labels: app: voyager + name: certificates.voyager.appscode.com spec: group: voyager.appscode.com names: - kind: Ingress - listKind: IngressList - plural: ingresses + kind: Certificate + listKind: CertificateList + plural: certificates shortNames: - - ing - singular: ingress + - cert + singular: certificate scope: Namespaced + validation: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + metadata: {} + spec: + properties: + acmeStagingURL: + description: ACME server that will be used to obtain this certificate. + Deprecated + type: string + acmeUserSecretName: + description: |- + Secret contains ACMEUser information. Secret must contain a key `email` If empty tries to find an Secret via domains if not found create an ACMEUser and stores as a secret. Secrets key to be expected: + ACME_EMAIL -> required, if not provided it will through error. + ACME_SERVER_URL -> custom server url to generate certificates, default is lets encrypt. + ACME_USER_DATA -> user data, if not found one will be created for the provided email, + and stored in the key. + type: string + challengeProvider: + properties: + dns: + properties: + credentialSecretName: + type: string + provider: + description: DNS Provider from the list https://github.com/appscode/voyager/blob/master/docs/tasks/certificate/providers.md + type: string + http: + properties: + ingress: + description: LocalTypedReference contains enough information + to let you inspect or modify the referred object. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + domains: + description: Tries to obtain a single certificate using all domains + passed into Domains. The first domain in domains is used for the CommonName + field of the certificate, all other domains are added using the Subject + Alternate Names extension. + items: + type: string + type: array + email: + description: Deprecated + type: string + httpProviderIngressReference: + description: LocalTypedReference contains enough information to let + you inspect or modify the referred object. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + provider: + description: Following fields are deprecated and will removed in future + version. https://github.com/appscode/voyager/pull/506 Deprecated. + DNS Provider. + type: string + providerCredentialSecretName: + description: ProviderCredentialSecretName is used to create the acme + client, that will do needed processing in DNS. Deprecated + type: string + storage: + properties: + secret: {} + vault: + properties: + name: + type: string + prefix: + type: string + required: + - challengeProvider + - acmeUserSecretName + status: + properties: + acmeUserSecretName: + description: Deprecated + type: string + certificateObtained: + description: Deprecated + type: boolean + conditions: + items: + properties: + lastUpdateTime: {} + message: + description: human readable message with details about the request + state + type: string + reason: + description: brief reason for the request state + type: string + type: + description: request approval state, currently Approved or Denied. + type: string + required: + - type + type: array + creationTime: {} + details: + properties: + accountRef: + type: string + certStableUrl: + type: string + certUrl: + type: string + domain: + type: string + required: + - domain + - certUrl + - certStableUrl + lastIssuedCertificate: + properties: + accountRef: + type: string + certStableURL: + type: string + certURL: + type: string + notAfter: {} + notBefore: {} + serialNumber: + type: string + required: + - certURL + - certStableURL + message: + description: Deprecated + type: string version: v1beta1 +status: + acceptedNames: + kind: "" + plural: "" + conditions: null diff --git a/apis/voyager/v1beta1/diff.go b/apis/voyager/v1beta1/diff.go index bd1836313..dbf066988 100644 --- a/apis/voyager/v1beta1/diff.go +++ b/apis/voyager/v1beta1/diff.go @@ -8,6 +8,7 @@ import ( "time" core_util "github.com/appscode/kutil/core/v1" + "github.com/appscode/voyager/apis/voyager" "github.com/google/go-cmp/cmp" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/api/resource" @@ -39,7 +40,7 @@ func (r Ingress) HasChanged(o Ingress) (bool, error) { return false, errors.New("not the same Ingress") } - if o.DeletionTimestamp != nil && core_util.HasFinalizer(o.ObjectMeta, VoyagerFinalizer) { + if o.DeletionTimestamp != nil && core_util.HasFinalizer(o.ObjectMeta, voyager.GroupName) { return true, nil } diff --git a/apis/voyager/v1beta1/ingress.go b/apis/voyager/v1beta1/ingress.go index 22b60ebc9..9bbc78173 100644 --- a/apis/voyager/v1beta1/ingress.go +++ b/apis/voyager/v1beta1/ingress.go @@ -7,9 +7,9 @@ import ( ) const ( - ResourceKindIngress = "Ingress" - ResourceNameIngress = "ingress" - ResourceTypeIngress = "ingresses" + ResourceKindIngress = "Ingress" + ResourceSingularIngress = "ingress" + ResourcePluralIngress = "ingresses" ) // +genclient diff --git a/glide.lock b/glide.lock index 95c8b63ca..a87dc71c3 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: c2dffc27b4f9fb7a28af11d0bad9bdf946b348b31237a5c328b77a953de4760f -updated: 2018-03-29T09:58:50.76079968-07:00 +hash: 1596963029d94145703f740f040faad597b7cc93be4e9933cb70a743c717b579 +updated: 2018-03-31T22:13:07.840552027-07:00 imports: - name: bitbucket.org/ww/goautoneg version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 @@ -25,7 +25,7 @@ imports: - types - version - name: github.com/appscode/jsonpatch - version: d1ada3d25b084ae42746377df856bbc74a901503 + version: 58a38a46ddb591a543c6b1003213353e29331c5a - name: github.com/appscode/kube-mon version: eeb8de0ba83795dc2fd86c3667d6566a62276160 subpackages: @@ -40,7 +40,7 @@ imports: - admission/v1beta1 - registry/admissionreview/v1beta1 - name: github.com/appscode/kutil - version: 9618833b480d6b78f02121dc4d231a933e30a8cc + version: 96378c9961f43b2720a241213363e4b379d99a04 subpackages: - apiextensions/v1beta1 - apps/v1beta1 @@ -152,7 +152,7 @@ imports: - name: github.com/dgrijalva/jwt-go version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20 - name: github.com/dnsimple/dnsimple-go - version: 67fc221205115459ce8a04043c7ca567072c92ec + version: 3a4b2e4e17f93fa9c2780f1c98b82da06fa8a826 subpackages: - dnsimple - name: github.com/elazarl/go-bindata-assetfs @@ -423,11 +423,11 @@ imports: subpackages: - mem - name: github.com/spf13/cobra - version: a1f051bc3eba734da4772d60e2d677f47cf93ef4 + version: 4dab30cb33e6633c33c787106bafbfbfdde7842d subpackages: - doc - name: github.com/spf13/pflag - version: e57e3eeb33f795204c1ca35f56c44f83227c6e66 + version: 1cd4a0c365d95803411bec89fb7b76bade17053b - name: github.com/timewasted/linode version: 37e84520dcf74488f67654f9c775b9752c232dc1 subpackages: diff --git a/glide.yaml b/glide.yaml index 5120a282b..2cf6bf11a 100644 --- a/glide.yaml +++ b/glide.yaml @@ -47,7 +47,7 @@ import: - package: github.com/spf13/cobra version: master - package: github.com/spf13/pflag - version: v1.0.0 + version: master - package: github.com/tredoe/osutil - package: github.com/xenolf/lego repo: https://github.com/appscode/lego.git diff --git a/hack/gencrd/main.go b/hack/gencrd/main.go new file mode 100644 index 000000000..50d077f3e --- /dev/null +++ b/hack/gencrd/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "os" + + "github.com/appscode/go/log" + "github.com/appscode/go/runtime" + crdutils "github.com/appscode/kutil/apiextensions/v1beta1" + api "github.com/appscode/voyager/apis/voyager/v1beta1" + crd_api "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" +) + +func main() { + filename := runtime.GOPath() + "/src/github.com/appscode/voyager/apis/voyager/v1beta1/crds.yaml" + + f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + log.Fatal(err) + } + defer f.Close() + + crds := []*crd_api.CustomResourceDefinition{ + api.Ingress{}.CustomResourceDefinition(), + api.Certificate{}.CustomResourceDefinition(), + } + for _, crd := range crds { + crdutils.MarshallCrd(f, crd, "yaml") + } +} diff --git a/pkg/operator/ingress_crds.go b/pkg/operator/ingress_crds.go index c5edc9732..aa9c0bbff 100644 --- a/pkg/operator/ingress_crds.go +++ b/pkg/operator/ingress_crds.go @@ -8,6 +8,7 @@ import ( core_util "github.com/appscode/kutil/core/v1" "github.com/appscode/kutil/meta" "github.com/appscode/kutil/tools/queue" + "github.com/appscode/voyager/apis/voyager" api "github.com/appscode/voyager/apis/voyager/v1beta1" "github.com/appscode/voyager/client/clientset/versioned/typed/voyager/v1beta1/util" "github.com/appscode/voyager/pkg/eventer" @@ -82,19 +83,19 @@ func (op *Operator) reconcileEngress(key string) error { ctrl := ingress.NewController(NewID(context.Background()), op.KubeClient, op.CRDClient, op.VoyagerClient, op.PromClient, op.svcLister, op.epLister, op.Config, engress) if engress.DeletionTimestamp != nil { - if core_util.HasFinalizer(engress.ObjectMeta, api.VoyagerFinalizer) { + if core_util.HasFinalizer(engress.ObjectMeta, voyager.GroupName) { glog.Infof("Delete for engress %s\n", key) ctrl.Delete() util.PatchIngress(op.VoyagerClient.VoyagerV1beta1(), engress, func(obj *api.Ingress) *api.Ingress { - obj.ObjectMeta = core_util.RemoveFinalizer(obj.ObjectMeta, api.VoyagerFinalizer) + obj.ObjectMeta = core_util.RemoveFinalizer(obj.ObjectMeta, voyager.GroupName) return obj }) } } else { glog.Infof("Sync/Add/Update for engress %s\n", key) - if !core_util.HasFinalizer(engress.ObjectMeta, api.VoyagerFinalizer) { + if !core_util.HasFinalizer(engress.ObjectMeta, voyager.GroupName) { util.PatchIngress(op.VoyagerClient.VoyagerV1beta1(), engress, func(obj *api.Ingress) *api.Ingress { - obj.ObjectMeta = core_util.AddFinalizer(obj.ObjectMeta, api.VoyagerFinalizer) + obj.ObjectMeta = core_util.AddFinalizer(obj.ObjectMeta, voyager.GroupName) return obj }) } diff --git a/pkg/operator/ingresses.go b/pkg/operator/ingresses.go index bfd7532a3..b6fdfc503 100644 --- a/pkg/operator/ingresses.go +++ b/pkg/operator/ingresses.go @@ -9,6 +9,7 @@ import ( ext_util "github.com/appscode/kutil/extensions/v1beta1" "github.com/appscode/kutil/meta" "github.com/appscode/kutil/tools/queue" + "github.com/appscode/voyager/apis/voyager" api "github.com/appscode/voyager/apis/voyager/v1beta1" "github.com/appscode/voyager/pkg/eventer" "github.com/appscode/voyager/pkg/ingress" @@ -95,19 +96,19 @@ func (op *Operator) reconcileIngress(key string) error { ctrl := ingress.NewController(NewID(context.Background()), op.KubeClient, op.CRDClient, op.VoyagerClient, op.PromClient, op.svcLister, op.epLister, op.Config, engress) if ing.DeletionTimestamp != nil { - if core_util.HasFinalizer(ing.ObjectMeta, api.VoyagerFinalizer) { + if core_util.HasFinalizer(ing.ObjectMeta, voyager.GroupName) { glog.Infof("Delete for engress %s\n", key) ctrl.Delete() ext_util.PatchIngress(op.KubeClient, ing, func(obj *extensions.Ingress) *extensions.Ingress { - obj.ObjectMeta = core_util.RemoveFinalizer(obj.ObjectMeta, api.VoyagerFinalizer) + obj.ObjectMeta = core_util.RemoveFinalizer(obj.ObjectMeta, voyager.GroupName) return obj }) } } else { glog.Infof("Sync/Add/Update for ingress %s\n", key) - if !core_util.HasFinalizer(ing.ObjectMeta, api.VoyagerFinalizer) { + if !core_util.HasFinalizer(ing.ObjectMeta, voyager.GroupName) { ext_util.PatchIngress(op.KubeClient, ing, func(obj *extensions.Ingress) *extensions.Ingress { - obj.ObjectMeta = core_util.AddFinalizer(obj.ObjectMeta, api.VoyagerFinalizer) + obj.ObjectMeta = core_util.AddFinalizer(obj.ObjectMeta, voyager.GroupName) return obj }) } diff --git a/vendor/github.com/appscode/kutil/apiextensions/v1beta1/cli-utils.go b/vendor/github.com/appscode/kutil/apiextensions/v1beta1/cli-utils.go new file mode 100644 index 000000000..c7b109843 --- /dev/null +++ b/vendor/github.com/appscode/kutil/apiextensions/v1beta1/cli-utils.go @@ -0,0 +1,151 @@ +// Copyright 2018 +// +// 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 v1beta1 + +import ( + "encoding/json" + "fmt" + "io" + "strings" + + "github.com/ghodss/yaml" + "github.com/spf13/pflag" + extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Config stores the user configuration input +type Config struct { + SpecDefinitionName string + EnableValidation bool + OutputFormat string + Labels Labels + Annotations Labels + ResourceScope string + Group string + Kind string + Version string + Plural string + Singular string + ShortNames []string + ListKind string + GetOpenAPIDefinitions GetAPIDefinitions +} + +type Labels struct { + LabelsString string + LabelsMap map[string]string +} + +func (labels *Labels) Type() string { return "Labels" } + +// Implement the flag.Value interface +func (labels *Labels) String() string { + return labels.LabelsString +} + +// Merge labels create a new map with labels merged. +func (labels *Labels) Merge(otherLabels map[string]string) map[string]string { + mergedLabels := map[string]string{} + + for key, value := range otherLabels { + mergedLabels[key] = value + } + + for key, value := range labels.LabelsMap { + mergedLabels[key] = value + } + return mergedLabels +} + +// Implement the flag.Set interface +func (labels *Labels) Set(value string) error { + m := map[string]string{} + if value != "" { + splited := strings.Split(value, ",") + for _, pair := range splited { + sp := strings.Split(pair, "=") + m[sp[0]] = sp[1] + } + } + (*labels).LabelsMap = m + (*labels).LabelsString = value + return nil +} + +func NewCustomResourceDefinition(config Config) *extensionsobj.CustomResourceDefinition { + crd := &extensionsobj.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: config.Plural + "." + config.Group, + Labels: config.Labels.LabelsMap, + Annotations: config.Annotations.LabelsMap, + }, + TypeMeta: CustomResourceDefinitionTypeMeta, + Spec: extensionsobj.CustomResourceDefinitionSpec{ + Group: config.Group, + Version: config.Version, + Scope: extensionsobj.ResourceScope(config.ResourceScope), + Names: extensionsobj.CustomResourceDefinitionNames{ + Plural: config.Plural, + Singular: config.Singular, + Kind: config.Kind, + ShortNames: config.ShortNames, + ListKind: config.ListKind, + }, + }, + } + + if config.SpecDefinitionName != "" && config.EnableValidation == true { + crd.Spec.Validation = GetCustomResourceValidation(config.SpecDefinitionName, config.GetOpenAPIDefinitions) + } + + return crd +} + +func MarshallCrd(w io.Writer, crd *extensionsobj.CustomResourceDefinition, outputFormat string) { + jsonBytes, err := json.MarshalIndent(crd, "", " ") + if err != nil { + fmt.Println("error:", err) + } + + if outputFormat == "json" { + w.Write(jsonBytes) + } else { + yamlBytes, err := yaml.JSONToYAML(jsonBytes) + if err != nil { + fmt.Println("error:", err) + } + w.Write([]byte("---\n")) + w.Write(yamlBytes) + } +} + +// InitFlags prepares command line flags parser +func InitFlags(cfg *Config, fs *pflag.FlagSet) *pflag.FlagSet { + fs.Var(&cfg.Labels, "labels", "Labels") + fs.Var(&cfg.Annotations, "annotations", "Annotations") + fs.BoolVar(&cfg.EnableValidation, "with-validation", true, "Add CRD validation field, default: true") + fs.StringVar(&cfg.Group, "apigroup", "custom.example.com", "CRD api group") + fs.StringVar(&cfg.SpecDefinitionName, "spec-name", "", "CRD spec definition name") + fs.StringVar(&cfg.OutputFormat, "output", "yaml", "output format: json|yaml") + fs.StringVar(&cfg.Kind, "kind", "", "CRD Kind") + fs.StringVar(&cfg.ResourceScope, "scope", string(extensionsobj.NamespaceScoped), "CRD scope: 'Namespaced' | 'Cluster'. Default: Namespaced") + fs.StringVar(&cfg.Version, "version", "v1", "CRD version, default: 'v1'") + fs.StringVar(&cfg.Plural, "plural", "", "CRD plural name") + fs.StringVar(&cfg.Singular, "singular", "", "CRD singular name") + fs.StringSliceVar(&cfg.ShortNames, "short-names", nil, "CRD short names") + fs.StringVar(&cfg.ListKind, "list-kind", "", "CRD list kind") + return fs +} diff --git a/vendor/github.com/appscode/kutil/apiextensions/v1beta1/convert_types.go b/vendor/github.com/appscode/kutil/apiextensions/v1beta1/convert_types.go new file mode 100644 index 000000000..25d411e63 --- /dev/null +++ b/vendor/github.com/appscode/kutil/apiextensions/v1beta1/convert_types.go @@ -0,0 +1,126 @@ +package v1beta1 + +import ( + "fmt" + + "github.com/go-openapi/spec" + extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + "k8s.io/kube-openapi/pkg/common" +) + +// SchemaPropsToJSONPropsArray converts []Schema to []JSONSchemaProps +func SchemaPropsToJSONPropsArray(schemas []spec.Schema, openapiSpec map[string]common.OpenAPIDefinition, nested bool) []extensionsobj.JSONSchemaProps { + var s []extensionsobj.JSONSchemaProps + for _, schema := range schemas { + s = append(s, *SchemaPropsToJSONProps(&schema, openapiSpec, nested)) + } + return s +} + +// StringOrArrayToString converts StringOrArray to string +func StringOrArrayToString(strOrArray spec.StringOrArray) string { + if len(strOrArray) > 0 { + return strOrArray[0] + } + return "" +} + +// EnumJSON converts []interface{} to []JSON +func EnumJSON(enum []interface{}) []extensionsobj.JSON { + var s []extensionsobj.JSON + for _, elt := range enum { + s = append(s, extensionsobj.JSON{ + Raw: []byte(fmt.Sprintf("%v", elt)), + }) + } + return s +} + +// SchemaOrArrayToJSONItems converts *SchemaOrArray to *JSONSchemaPropsOrArray +func SchemaOrArrayToJSONItems(schemaOrArray *spec.SchemaOrArray, openapiSpec map[string]common.OpenAPIDefinition, nested bool) *extensionsobj.JSONSchemaPropsOrArray { + var array *extensionsobj.JSONSchemaPropsOrArray + if schemaOrArray == nil { + return array + } + return &extensionsobj.JSONSchemaPropsOrArray{ + Schema: SchemaPropsToJSONProps(schemaOrArray.Schema, openapiSpec, nested), + JSONSchemas: SchemaPropsToJSONPropsArray(schemaOrArray.Schemas, openapiSpec, nested), + } +} + +// SchemaOrBoolToJSONProps converts *SchemaOrBool to *JSONSchemaPropsOrBool +func SchemaOrBoolToJSONProps(schemaOrBool *spec.SchemaOrBool, openapiSpec map[string]common.OpenAPIDefinition, nested bool) *extensionsobj.JSONSchemaPropsOrBool { + var s *extensionsobj.JSONSchemaPropsOrBool + if schemaOrBool == nil { + return s + } + return &extensionsobj.JSONSchemaPropsOrBool{ + Schema: SchemaPropsToJSONProps(schemaOrBool.Schema, openapiSpec, nested), + Allows: schemaOrBool.Allows, + } +} + +// SchemPropsMapToJSONMap converts map[string]Schema to map[string]JSONSchemaProps +func SchemPropsMapToJSONMap(schemaMap map[string]spec.Schema, openapiSpec map[string]common.OpenAPIDefinition, nested bool) map[string]extensionsobj.JSONSchemaProps { + var m map[string]extensionsobj.JSONSchemaProps + m = make(map[string]extensionsobj.JSONSchemaProps) + for key, schema := range schemaMap { + m[key] = *SchemaPropsToJSONProps(&schema, openapiSpec, nested) + } + return m +} + +// SchemaPropsToJSONProps converts a SchemaProps to a JSONProps +func SchemaPropsToJSONProps(schema *spec.Schema, openapiSpec map[string]common.OpenAPIDefinition, nested bool) *extensionsobj.JSONSchemaProps { + var props *extensionsobj.JSONSchemaProps + if schema == nil { + return props + } + schemaProps := &schema.SchemaProps + + var ref *string + if schemaProps.Ref.String() != "" { + if nested { + propref := openapiSpec[schemaProps.Ref.String()].Schema + // If nested just return a pointer to the reference + return SchemaPropsToJSONProps(&propref, openapiSpec, nested) + } + ref = new(string) + *ref = schemaProps.Ref.String() + } + + props = &extensionsobj.JSONSchemaProps{ + Ref: ref, + ID: schemaProps.ID, + Schema: extensionsobj.JSONSchemaURL(string(schema.Schema)), + Description: schemaProps.Description, + Type: StringOrArrayToString(schemaProps.Type), + Format: schemaProps.Format, + Title: schemaProps.Title, + Maximum: schemaProps.Maximum, + ExclusiveMaximum: schemaProps.ExclusiveMaximum, + Minimum: schemaProps.Minimum, + ExclusiveMinimum: schemaProps.ExclusiveMinimum, + MaxLength: schemaProps.MaxLength, + MinLength: schemaProps.MinLength, + Pattern: schemaProps.Pattern, + MaxItems: schemaProps.MaxItems, + MinItems: schemaProps.MinItems, + UniqueItems: schemaProps.UniqueItems, + MultipleOf: schemaProps.MultipleOf, + Enum: EnumJSON(schemaProps.Enum), + MaxProperties: schemaProps.MaxProperties, + MinProperties: schemaProps.MinProperties, + Required: schemaProps.Required, + Items: SchemaOrArrayToJSONItems(schemaProps.Items, openapiSpec, nested), + AllOf: SchemaPropsToJSONPropsArray(schemaProps.AllOf, openapiSpec, nested), + OneOf: SchemaPropsToJSONPropsArray(schemaProps.OneOf, openapiSpec, nested), + AnyOf: SchemaPropsToJSONPropsArray(schemaProps.AnyOf, openapiSpec, nested), + Not: SchemaPropsToJSONProps(schemaProps.Not, openapiSpec, nested), + Properties: SchemPropsMapToJSONMap(schemaProps.Properties, openapiSpec, nested), + AdditionalProperties: SchemaOrBoolToJSONProps(schemaProps.AdditionalProperties, openapiSpec, nested), + PatternProperties: SchemPropsMapToJSONMap(schemaProps.PatternProperties, openapiSpec, nested), + AdditionalItems: SchemaOrBoolToJSONProps(schemaProps.AdditionalItems, openapiSpec, nested), + } + return props +} diff --git a/vendor/github.com/appscode/kutil/apiextensions/v1beta1/crdvalidation.go b/vendor/github.com/appscode/kutil/apiextensions/v1beta1/crdvalidation.go new file mode 100644 index 000000000..174538b42 --- /dev/null +++ b/vendor/github.com/appscode/kutil/apiextensions/v1beta1/crdvalidation.go @@ -0,0 +1,46 @@ +package v1beta1 + +import ( + "github.com/go-openapi/spec" + extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kube-openapi/pkg/common" +) + +// CustomResourceDefinitionTypeMeta set the default kind/apiversion of CRD +var CustomResourceDefinitionTypeMeta = metav1.TypeMeta{ + Kind: "CustomResourceDefinition", + APIVersion: "apiextensions.k8s.io/v1beta1", +} + +// OpenAPIRefCallBack returns a jsonref using the input string without modification +func OpenAPIRefCallBack(name string) spec.Ref { + return spec.MustCreateRef(name) +} + +// GetAPIDefinition is a function returning a map with all Definition +type GetAPIDefinitions func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition + +// GetCustomResourceValidations returns a CRD validation spec map. It took the openapi generated definition from kube-openapi as argument +func GetCustomResourceValidations(fn GetAPIDefinitions) map[string]*extensionsobj.CustomResourceValidation { + openapiSpec := fn(OpenAPIRefCallBack) + var definitions map[string]*extensionsobj.CustomResourceValidation + definitions = make(map[string]*extensionsobj.CustomResourceValidation) + for key, definition := range openapiSpec { + schema := definition.Schema + definitions[key] = &extensionsobj.CustomResourceValidation{ + OpenAPIV3Schema: SchemaPropsToJSONProps(&schema, openapiSpec, true), + } + } + return definitions +} + +// GetCustomResourceValidation returns the validation definition for a CRD name +func GetCustomResourceValidation(name string, fn func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition) *extensionsobj.CustomResourceValidation { + openapiSpec := fn(OpenAPIRefCallBack) + schema := openapiSpec[name].Schema + return &extensionsobj.CustomResourceValidation{ + OpenAPIV3Schema: SchemaPropsToJSONProps(&schema, openapiSpec, true), + } + +} diff --git a/vendor/github.com/appscode/kutil/apiextensions/v1beta1/kubernetes.go b/vendor/github.com/appscode/kutil/apiextensions/v1beta1/kubernetes.go index a5f6acfb1..296293cea 100644 --- a/vendor/github.com/appscode/kutil/apiextensions/v1beta1/kubernetes.go +++ b/vendor/github.com/appscode/kutil/apiextensions/v1beta1/kubernetes.go @@ -24,6 +24,11 @@ func RegisterCRDs(client crd_cs.ApiextensionsV1beta1Interface, crds []*crd_api.C } } else if err != nil { return err + } else { + _, err = client.CustomResourceDefinitions().Update(crd) + if err != nil { + return err + } } } return WaitForCRDReady(client.RESTClient(), crds) diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go index 15b811279..34d1bf367 100644 --- a/vendor/github.com/spf13/cobra/command.go +++ b/vendor/github.com/spf13/cobra/command.go @@ -27,6 +27,9 @@ import ( flag "github.com/spf13/pflag" ) +// FParseErrWhitelist configures Flag parse errors to be ignored +type FParseErrWhitelist flag.ParseErrorsWhitelist + // Command is just that, a command for your application. // E.g. 'go run ...' - 'run' is the command. Cobra requires // you to define the usage and description as part of your command @@ -137,6 +140,9 @@ type Command struct { // TraverseChildren parses flags on all parents before executing child command. TraverseChildren bool + //FParseErrWhitelist flag parse errors to be ignored + FParseErrWhitelist FParseErrWhitelist + // commands is the list of commands supported by this program. commands []*Command // parent is a parent command for this command. @@ -1463,6 +1469,10 @@ func (c *Command) ParseFlags(args []string) error { } beforeErrorBufLen := c.flagErrorBuf.Len() c.mergePersistentFlags() + + //do it here after merging all flags and just before parse + c.Flags().ParseErrorsWhitelist = flag.ParseErrorsWhitelist(c.FParseErrWhitelist) + err := c.Flags().Parse(args) // Print warnings if they occurred (e.g. deprecated flag messages). if c.flagErrorBuf.Len()-beforeErrorBufLen > 0 && err == nil { diff --git a/vendor/github.com/spf13/pflag/bytes.go b/vendor/github.com/spf13/pflag/bytes.go new file mode 100644 index 000000000..12c58db9f --- /dev/null +++ b/vendor/github.com/spf13/pflag/bytes.go @@ -0,0 +1,105 @@ +package pflag + +import ( + "encoding/hex" + "fmt" + "strings" +) + +// BytesHex adapts []byte for use as a flag. Value of flag is HEX encoded +type bytesHexValue []byte + +func (bytesHex bytesHexValue) String() string { + return fmt.Sprintf("%X", []byte(bytesHex)) +} + +func (bytesHex *bytesHexValue) Set(value string) error { + bin, err := hex.DecodeString(strings.TrimSpace(value)) + + if err != nil { + return err + } + + *bytesHex = bin + + return nil +} + +func (*bytesHexValue) Type() string { + return "bytesHex" +} + +func newBytesHexValue(val []byte, p *[]byte) *bytesHexValue { + *p = val + return (*bytesHexValue)(p) +} + +func bytesHexConv(sval string) (interface{}, error) { + + bin, err := hex.DecodeString(sval) + + if err == nil { + return bin, nil + } + + return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err) +} + +// GetBytesHex return the []byte value of a flag with the given name +func (f *FlagSet) GetBytesHex(name string) ([]byte, error) { + val, err := f.getFlagType(name, "bytesHex", bytesHexConv) + + if err != nil { + return []byte{}, err + } + + return val.([]byte), nil +} + +// BytesHexVar defines an []byte flag with specified name, default value, and usage string. +// The argument p points to an []byte variable in which to store the value of the flag. +func (f *FlagSet) BytesHexVar(p *[]byte, name string, value []byte, usage string) { + f.VarP(newBytesHexValue(value, p), name, "", usage) +} + +// BytesHexVarP is like BytesHexVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BytesHexVarP(p *[]byte, name, shorthand string, value []byte, usage string) { + f.VarP(newBytesHexValue(value, p), name, shorthand, usage) +} + +// BytesHexVar defines an []byte flag with specified name, default value, and usage string. +// The argument p points to an []byte variable in which to store the value of the flag. +func BytesHexVar(p *[]byte, name string, value []byte, usage string) { + CommandLine.VarP(newBytesHexValue(value, p), name, "", usage) +} + +// BytesHexVarP is like BytesHexVar, but accepts a shorthand letter that can be used after a single dash. +func BytesHexVarP(p *[]byte, name, shorthand string, value []byte, usage string) { + CommandLine.VarP(newBytesHexValue(value, p), name, shorthand, usage) +} + +// BytesHex defines an []byte flag with specified name, default value, and usage string. +// The return value is the address of an []byte variable that stores the value of the flag. +func (f *FlagSet) BytesHex(name string, value []byte, usage string) *[]byte { + p := new([]byte) + f.BytesHexVarP(p, name, "", value, usage) + return p +} + +// BytesHexP is like BytesHex, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BytesHexP(name, shorthand string, value []byte, usage string) *[]byte { + p := new([]byte) + f.BytesHexVarP(p, name, shorthand, value, usage) + return p +} + +// BytesHex defines an []byte flag with specified name, default value, and usage string. +// The return value is the address of an []byte variable that stores the value of the flag. +func BytesHex(name string, value []byte, usage string) *[]byte { + return CommandLine.BytesHexP(name, "", value, usage) +} + +// BytesHexP is like BytesHex, but accepts a shorthand letter that can be used after a single dash. +func BytesHexP(name, shorthand string, value []byte, usage string) *[]byte { + return CommandLine.BytesHexP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/count.go b/vendor/github.com/spf13/pflag/count.go index 250a43814..aa126e44d 100644 --- a/vendor/github.com/spf13/pflag/count.go +++ b/vendor/github.com/spf13/pflag/count.go @@ -11,13 +11,13 @@ func newCountValue(val int, p *int) *countValue { } func (i *countValue) Set(s string) error { - v, err := strconv.ParseInt(s, 0, 64) - // -1 means that no specific value was passed, so increment - if v == -1 { + // "+1" means that no specific value was passed, so increment + if s == "+1" { *i = countValue(*i + 1) - } else { - *i = countValue(v) + return nil } + v, err := strconv.ParseInt(s, 0, 0) + *i = countValue(v) return err } @@ -54,7 +54,7 @@ func (f *FlagSet) CountVar(p *int, name string, usage string) { // CountVarP is like CountVar only take a shorthand for the flag name. func (f *FlagSet) CountVarP(p *int, name, shorthand string, usage string) { flag := f.VarPF(newCountValue(0, p), name, shorthand, usage) - flag.NoOptDefVal = "-1" + flag.NoOptDefVal = "+1" } // CountVar like CountVar only the flag is placed on the CommandLine instead of a given flag set diff --git a/vendor/github.com/spf13/pflag/duration_slice.go b/vendor/github.com/spf13/pflag/duration_slice.go new file mode 100644 index 000000000..52c6b6dc1 --- /dev/null +++ b/vendor/github.com/spf13/pflag/duration_slice.go @@ -0,0 +1,128 @@ +package pflag + +import ( + "fmt" + "strings" + "time" +) + +// -- durationSlice Value +type durationSliceValue struct { + value *[]time.Duration + changed bool +} + +func newDurationSliceValue(val []time.Duration, p *[]time.Duration) *durationSliceValue { + dsv := new(durationSliceValue) + dsv.value = p + *dsv.value = val + return dsv +} + +func (s *durationSliceValue) Set(val string) error { + ss := strings.Split(val, ",") + out := make([]time.Duration, len(ss)) + for i, d := range ss { + var err error + out[i], err = time.ParseDuration(d) + if err != nil { + return err + } + + } + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + s.changed = true + return nil +} + +func (s *durationSliceValue) Type() string { + return "durationSlice" +} + +func (s *durationSliceValue) String() string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = fmt.Sprintf("%s", d) + } + return "[" + strings.Join(out, ",") + "]" +} + +func durationSliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []time.Duration{}, nil + } + ss := strings.Split(val, ",") + out := make([]time.Duration, len(ss)) + for i, d := range ss { + var err error + out[i], err = time.ParseDuration(d) + if err != nil { + return nil, err + } + + } + return out, nil +} + +// GetDurationSlice returns the []time.Duration value of a flag with the given name +func (f *FlagSet) GetDurationSlice(name string) ([]time.Duration, error) { + val, err := f.getFlagType(name, "durationSlice", durationSliceConv) + if err != nil { + return []time.Duration{}, err + } + return val.([]time.Duration), nil +} + +// DurationSliceVar defines a durationSlice flag with specified name, default value, and usage string. +// The argument p points to a []time.Duration variable in which to store the value of the flag. +func (f *FlagSet) DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) { + f.VarP(newDurationSliceValue(value, p), name, "", usage) +} + +// DurationSliceVarP is like DurationSliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) DurationSliceVarP(p *[]time.Duration, name, shorthand string, value []time.Duration, usage string) { + f.VarP(newDurationSliceValue(value, p), name, shorthand, usage) +} + +// DurationSliceVar defines a duration[] flag with specified name, default value, and usage string. +// The argument p points to a duration[] variable in which to store the value of the flag. +func DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) { + CommandLine.VarP(newDurationSliceValue(value, p), name, "", usage) +} + +// DurationSliceVarP is like DurationSliceVar, but accepts a shorthand letter that can be used after a single dash. +func DurationSliceVarP(p *[]time.Duration, name, shorthand string, value []time.Duration, usage string) { + CommandLine.VarP(newDurationSliceValue(value, p), name, shorthand, usage) +} + +// DurationSlice defines a []time.Duration flag with specified name, default value, and usage string. +// The return value is the address of a []time.Duration variable that stores the value of the flag. +func (f *FlagSet) DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration { + p := []time.Duration{} + f.DurationSliceVarP(&p, name, "", value, usage) + return &p +} + +// DurationSliceP is like DurationSlice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) DurationSliceP(name, shorthand string, value []time.Duration, usage string) *[]time.Duration { + p := []time.Duration{} + f.DurationSliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// DurationSlice defines a []time.Duration flag with specified name, default value, and usage string. +// The return value is the address of a []time.Duration variable that stores the value of the flag. +func DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration { + return CommandLine.DurationSliceP(name, "", value, usage) +} + +// DurationSliceP is like DurationSlice, but accepts a shorthand letter that can be used after a single dash. +func DurationSliceP(name, shorthand string, value []time.Duration, usage string) *[]time.Duration { + return CommandLine.DurationSliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/flag.go b/vendor/github.com/spf13/pflag/flag.go index 6f1fc3007..f0acbb84e 100644 --- a/vendor/github.com/spf13/pflag/flag.go +++ b/vendor/github.com/spf13/pflag/flag.go @@ -123,6 +123,12 @@ const ( PanicOnError ) +// ParseErrorsWhitelist defines the parsing errors that can be ignored +type ParseErrorsWhitelist struct { + // UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags + UnknownFlags bool +} + // NormalizedName is a flag name that has been normalized according to rules // for the FlagSet (e.g. making '-' and '_' equivalent). type NormalizedName string @@ -138,6 +144,9 @@ type FlagSet struct { // help/usage messages. SortFlags bool + // ParseErrorsWhitelist is used to configure a whitelist of errors + ParseErrorsWhitelist ParseErrorsWhitelist + name string parsed bool actual map[NormalizedName]*Flag @@ -202,12 +211,18 @@ func sortFlags(flags map[NormalizedName]*Flag) []*Flag { func (f *FlagSet) SetNormalizeFunc(n func(f *FlagSet, name string) NormalizedName) { f.normalizeNameFunc = n f.sortedFormal = f.sortedFormal[:0] - for k, v := range f.orderedFormal { - delete(f.formal, NormalizedName(v.Name)) - nname := f.normalizeFlagName(v.Name) - v.Name = string(nname) - f.formal[nname] = v - f.orderedFormal[k] = v + for fname, flag := range f.formal { + nname := f.normalizeFlagName(flag.Name) + if fname == nname { + continue + } + flag.Name = string(nname) + delete(f.formal, fname) + f.formal[nname] = flag + if _, set := f.actual[fname]; set { + delete(f.actual, fname) + f.actual[nname] = flag + } } } @@ -440,13 +455,15 @@ func (f *FlagSet) Set(name, value string) error { return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err) } - if f.actual == nil { - f.actual = make(map[NormalizedName]*Flag) - } - f.actual[normalName] = flag - f.orderedActual = append(f.orderedActual, flag) + if !flag.Changed { + if f.actual == nil { + f.actual = make(map[NormalizedName]*Flag) + } + f.actual[normalName] = flag + f.orderedActual = append(f.orderedActual, flag) - flag.Changed = true + flag.Changed = true + } if flag.Deprecated != "" { fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) @@ -556,6 +573,14 @@ func UnquoteUsage(flag *Flag) (name string, usage string) { name = "int" case "uint64": name = "uint" + case "stringSlice": + name = "strings" + case "intSlice": + name = "ints" + case "uintSlice": + name = "uints" + case "boolSlice": + name = "bools" } return @@ -570,11 +595,14 @@ func wrapN(i, slop int, s string) (string, string) { return s, "" } - w := strings.LastIndexAny(s[:i], " \t") + w := strings.LastIndexAny(s[:i], " \t\n") if w <= 0 { return s, "" } - + nlPos := strings.LastIndex(s[:i], "\n") + if nlPos > 0 && nlPos < w { + return s[:nlPos], s[nlPos+1:] + } return s[:w], s[w+1:] } @@ -583,7 +611,7 @@ func wrapN(i, slop int, s string) (string, string) { // caller). Pass `w` == 0 to do no wrapping func wrap(i, w int, s string) string { if w == 0 { - return s + return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1) } // space between indent i and end of line width w into which @@ -601,7 +629,7 @@ func wrap(i, w int, s string) string { } // If still not enough space then don't even try to wrap. if wrap < 24 { - return s + return strings.Replace(s, "\n", r, -1) } // Try to avoid short orphan words on the final line, by @@ -613,14 +641,14 @@ func wrap(i, w int, s string) string { // Handle first line, which is indented by the caller (or the // special case above) l, s = wrapN(wrap, slop, s) - r = r + l + r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1) // Now wrap the rest for s != "" { var t string t, s = wrapN(wrap, slop, s) - r = r + "\n" + strings.Repeat(" ", i) + t + r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1) } return r @@ -660,6 +688,10 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string { if flag.NoOptDefVal != "true" { line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) } + case "count": + if flag.NoOptDefVal != "+1" { + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } default: line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) } @@ -857,8 +889,10 @@ func VarP(value Value, name, shorthand, usage string) { // returns the error. func (f *FlagSet) failf(format string, a ...interface{}) error { err := fmt.Errorf(format, a...) - fmt.Fprintln(f.out(), err) - f.usage() + if f.errorHandling != ContinueOnError { + fmt.Fprintln(f.out(), err) + f.usage() + } return err } @@ -874,6 +908,25 @@ func (f *FlagSet) usage() { } } +//--unknown (args will be empty) +//--unknown --next-flag ... (args will be --next-flag ...) +//--unknown arg ... (args will be arg ...) +func stripUnknownFlagValue(args []string) []string { + if len(args) == 0 { + //--unknown + return args + } + + first := args[0] + if first[0] == '-' { + //--unknown --next-flag ... + return args + } + + //--unknown arg ... (args will be arg ...) + return args[1:] +} + func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) { a = args name := s[2:] @@ -885,13 +938,24 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin split := strings.SplitN(name, "=", 2) name = split[0] flag, exists := f.formal[f.normalizeFlagName(name)] + if !exists { - if name == "help" { // special case for nice help message. + switch { + case name == "help": f.usage() return a, ErrHelp + case f.ParseErrorsWhitelist.UnknownFlags: + // --unknown=unknownval arg ... + // we do not want to lose arg in this case + if len(split) >= 2 { + return a, nil + } + + return stripUnknownFlagValue(a), nil + default: + err = f.failf("unknown flag: --%s", name) + return } - err = f.failf("unknown flag: --%s", name) - return } var value string @@ -912,6 +976,9 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin } err = fn(flag, value) + if err != nil { + f.failf(err.Error()) + } return } @@ -926,13 +993,25 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse flag, exists := f.shorthands[c] if !exists { - if c == 'h' { // special case for nice help message. + switch { + case c == 'h': f.usage() err = ErrHelp return + case f.ParseErrorsWhitelist.UnknownFlags: + // '-f=arg arg ...' + // we do not want to lose arg in this case + if len(shorthands) > 2 && shorthands[1] == '=' { + outShorts = "" + return + } + + outArgs = stripUnknownFlagValue(outArgs) + return + default: + err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands) + return } - err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands) - return } var value string @@ -962,6 +1041,9 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse } err = fn(flag, value) + if err != nil { + f.failf(err.Error()) + } return } @@ -1034,6 +1116,7 @@ func (f *FlagSet) Parse(arguments []string) error { case ContinueOnError: return err case ExitOnError: + fmt.Println(err) os.Exit(2) case PanicOnError: panic(err) diff --git a/vendor/github.com/spf13/pflag/int16.go b/vendor/github.com/spf13/pflag/int16.go new file mode 100644 index 000000000..f1a01d05e --- /dev/null +++ b/vendor/github.com/spf13/pflag/int16.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- int16 Value +type int16Value int16 + +func newInt16Value(val int16, p *int16) *int16Value { + *p = val + return (*int16Value)(p) +} + +func (i *int16Value) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 16) + *i = int16Value(v) + return err +} + +func (i *int16Value) Type() string { + return "int16" +} + +func (i *int16Value) String() string { return strconv.FormatInt(int64(*i), 10) } + +func int16Conv(sval string) (interface{}, error) { + v, err := strconv.ParseInt(sval, 0, 16) + if err != nil { + return 0, err + } + return int16(v), nil +} + +// GetInt16 returns the int16 value of a flag with the given name +func (f *FlagSet) GetInt16(name string) (int16, error) { + val, err := f.getFlagType(name, "int16", int16Conv) + if err != nil { + return 0, err + } + return val.(int16), nil +} + +// Int16Var defines an int16 flag with specified name, default value, and usage string. +// The argument p points to an int16 variable in which to store the value of the flag. +func (f *FlagSet) Int16Var(p *int16, name string, value int16, usage string) { + f.VarP(newInt16Value(value, p), name, "", usage) +} + +// Int16VarP is like Int16Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int16VarP(p *int16, name, shorthand string, value int16, usage string) { + f.VarP(newInt16Value(value, p), name, shorthand, usage) +} + +// Int16Var defines an int16 flag with specified name, default value, and usage string. +// The argument p points to an int16 variable in which to store the value of the flag. +func Int16Var(p *int16, name string, value int16, usage string) { + CommandLine.VarP(newInt16Value(value, p), name, "", usage) +} + +// Int16VarP is like Int16Var, but accepts a shorthand letter that can be used after a single dash. +func Int16VarP(p *int16, name, shorthand string, value int16, usage string) { + CommandLine.VarP(newInt16Value(value, p), name, shorthand, usage) +} + +// Int16 defines an int16 flag with specified name, default value, and usage string. +// The return value is the address of an int16 variable that stores the value of the flag. +func (f *FlagSet) Int16(name string, value int16, usage string) *int16 { + p := new(int16) + f.Int16VarP(p, name, "", value, usage) + return p +} + +// Int16P is like Int16, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int16P(name, shorthand string, value int16, usage string) *int16 { + p := new(int16) + f.Int16VarP(p, name, shorthand, value, usage) + return p +} + +// Int16 defines an int16 flag with specified name, default value, and usage string. +// The return value is the address of an int16 variable that stores the value of the flag. +func Int16(name string, value int16, usage string) *int16 { + return CommandLine.Int16P(name, "", value, usage) +} + +// Int16P is like Int16, but accepts a shorthand letter that can be used after a single dash. +func Int16P(name, shorthand string, value int16, usage string) *int16 { + return CommandLine.Int16P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/string_array.go b/vendor/github.com/spf13/pflag/string_array.go index 276b7ed49..fa7bc6018 100644 --- a/vendor/github.com/spf13/pflag/string_array.go +++ b/vendor/github.com/spf13/pflag/string_array.go @@ -52,7 +52,7 @@ func (f *FlagSet) GetStringArray(name string) ([]string, error) { // StringArrayVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the values of the multiple flags. -// The value of each argument will not try to be separated by comma +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. func (f *FlagSet) StringArrayVar(p *[]string, name string, value []string, usage string) { f.VarP(newStringArrayValue(value, p), name, "", usage) } @@ -64,7 +64,7 @@ func (f *FlagSet) StringArrayVarP(p *[]string, name, shorthand string, value []s // StringArrayVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the value of the flag. -// The value of each argument will not try to be separated by comma +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. func StringArrayVar(p *[]string, name string, value []string, usage string) { CommandLine.VarP(newStringArrayValue(value, p), name, "", usage) } @@ -76,7 +76,7 @@ func StringArrayVarP(p *[]string, name, shorthand string, value []string, usage // StringArray defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. -// The value of each argument will not try to be separated by comma +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. func (f *FlagSet) StringArray(name string, value []string, usage string) *[]string { p := []string{} f.StringArrayVarP(&p, name, "", value, usage) @@ -92,7 +92,7 @@ func (f *FlagSet) StringArrayP(name, shorthand string, value []string, usage str // StringArray defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. -// The value of each argument will not try to be separated by comma +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. func StringArray(name string, value []string, usage string) *[]string { return CommandLine.StringArrayP(name, "", value, usage) } diff --git a/vendor/github.com/spf13/pflag/string_slice.go b/vendor/github.com/spf13/pflag/string_slice.go index 05eee7543..0cd3ccc08 100644 --- a/vendor/github.com/spf13/pflag/string_slice.go +++ b/vendor/github.com/spf13/pflag/string_slice.go @@ -82,6 +82,11 @@ func (f *FlagSet) GetStringSlice(name string) ([]string, error) { // StringSliceVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" -ss="v3" +// will result in +// []string{"v1", "v2", "v3"} func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) { f.VarP(newStringSliceValue(value, p), name, "", usage) } @@ -93,6 +98,11 @@ func (f *FlagSet) StringSliceVarP(p *[]string, name, shorthand string, value []s // StringSliceVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" -ss="v3" +// will result in +// []string{"v1", "v2", "v3"} func StringSliceVar(p *[]string, name string, value []string, usage string) { CommandLine.VarP(newStringSliceValue(value, p), name, "", usage) } @@ -104,6 +114,11 @@ func StringSliceVarP(p *[]string, name, shorthand string, value []string, usage // StringSlice defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" -ss="v3" +// will result in +// []string{"v1", "v2", "v3"} func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string { p := []string{} f.StringSliceVarP(&p, name, "", value, usage) @@ -119,6 +134,11 @@ func (f *FlagSet) StringSliceP(name, shorthand string, value []string, usage str // StringSlice defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" -ss="v3" +// will result in +// []string{"v1", "v2", "v3"} func StringSlice(name string, value []string, usage string) *[]string { return CommandLine.StringSliceP(name, "", value, usage) }